# Agents: chat & stream Source: https://docs.noxus.ai/api-reference/cookbook/agents Create a conversation with an agent, then chat, stream replies, or tail events Agents are driven through **conversations**. You create a conversation bound to an agent, then send messages to it: * **Create** — `POST /v1/conversations?assistant_id={agent_id}`. * **Chat (blocking)** — `POST /v1/conversations/{conversation_id}/chat` returns the final reply. * **Stream a reply** — `POST /v1/conversations/{conversation_id}/stream` (Server-Sent Events). * **Tail events** — `GET /v1/conversations/{conversation_id}/events` (SSE for a run started elsewhere). The streaming endpoints accept `?format=json` for normalised `{event, data}` envelopes; omit it to receive raw Vercel AI SDK frames. Replace `agent_id` and `your_api_key` below. ## Send a message and get the reply (blocking) Simplest request/response. `chat` blocks until the agent finishes and returns its final message — use it when you only need the answer, not the intermediate steps. ```python Python SDK theme={null} # pip install noxus-sdk from noxus_sdk.client import Client from noxus_sdk.resources.conversations import MessageRequest client = Client(api_key="your_api_key") conversation = client.conversations.create("My Conversation", agent_id="agent_id") print(f"conversation: {conversation.id}") reply = conversation.chat(MessageRequest(content="Hello!")) print(reply.parts) ``` ```python Python REST theme={null} import requests base = "https://backend.noxus.ai" headers = {"X-API-KEY": "your_api_key", "Content-Type": "application/json"} # 1. Create the conversation conv = requests.post( f"{base}/v1/conversations?assistant_id=agent_id", json={"name": "My Conversation"}, headers=headers, ).json() cid = conv["id"] # 2. Chat (blocking) — returns the agent's last message reply = requests.post( f"{base}/v1/conversations/{cid}/chat?assistant_id=agent_id", json={"content": "Hello!"}, headers=headers, ).json() print(reply["parts"]) ``` ```javascript Node theme={null} const base = "https://backend.noxus.ai"; const headers = { "X-API-KEY": "your_api_key", "Content-Type": "application/json" }; // 1. Create the conversation const conv = await fetch(`${base}/v1/conversations?assistant_id=agent_id`, { method: "POST", headers, body: JSON.stringify({ name: "My Conversation" }), }).then((r) => r.json()); // 2. Chat (blocking) const reply = await fetch( `${base}/v1/conversations/${conv.id}/chat?assistant_id=agent_id`, { method: "POST", headers, body: JSON.stringify({ content: "Hello!" }) } ).then((r) => r.json()); console.log(reply.parts); ``` ```bash cURL theme={null} #!/bin/bash base="https://backend.noxus.ai" # 1. Create the conversation cid=$(curl -s -X POST "$base/v1/conversations?assistant_id=agent_id" \ -H "X-API-KEY: your_api_key" -H "Content-Type: application/json" \ -d '{"name": "My Conversation"}' | jq -r '.id') # 2. Chat (blocking) curl -X POST "$base/v1/conversations/$cid/chat?assistant_id=agent_id" \ -H "X-API-KEY: your_api_key" -H "Content-Type: application/json" \ -d '{"content": "Hello!"}' ``` ## Stream the reply token-by-token (SSE) Streams the agent's response as it is generated. Best for chat UIs — render text deltas, tool calls, and steps as they arrive instead of waiting for the whole answer. Events look like `text-delta`, `finish-step`, etc. ```python Python SDK theme={null} from noxus_sdk.client import Client from noxus_sdk.resources.conversations import MessageRequest client = Client(api_key="your_api_key") conversation = client.conversations.create("My Conversation", agent_id="agent_id") for event in conversation.stream(MessageRequest(content="Hello!")): print(event.event, event.data) # e.g. "text-delta", {...} ``` ```python Python REST theme={null} import json import requests base = "https://backend.noxus.ai" cid = "" # from the create step headers = {"X-API-KEY": "your_api_key", "Content-Type": "application/json"} with requests.post( f"{base}/v1/conversations/{cid}/stream?assistant_id=agent_id&format=json", json={"content": "Hello!"}, headers=headers, stream=True, ) as resp: for line in resp.iter_lines(): if line and line.startswith(b"data:"): print(json.loads(line[len(b"data:"):])) ``` ```javascript Node theme={null} const base = "https://backend.noxus.ai"; const cid = ""; // from the create step const resp = await fetch( `${base}/v1/conversations/${cid}/stream?assistant_id=agent_id&format=json`, { method: "POST", headers: { "X-API-KEY": "your_api_key", "Content-Type": "application/json" }, body: JSON.stringify({ content: "Hello!" }), } ); const reader = resp.body.getReader(); const decoder = new TextDecoder(); while (true) { const { done, value } = await reader.read(); if (done) break; process.stdout.write(decoder.decode(value)); // SSE frames } ``` ```bash cURL theme={null} #!/bin/bash base="https://backend.noxus.ai" cid="" # from the create step # -N disables buffering so deltas arrive live curl -N -X POST "$base/v1/conversations/$cid/stream?assistant_id=agent_id&format=json" \ -H "X-API-KEY: your_api_key" -H "Content-Type: application/json" \ -d '{"content": "Hello!"}' ``` ## Tail an in-progress conversation (SSE) Attach to the live event stream of a run that was started elsewhere — for example a message you sent asynchronously, or a run shared across workers. Pass `?etag=` to resume from a specific point in the stream. ```python Python SDK theme={null} from noxus_sdk.client import Client client = Client(api_key="your_api_key") conversation = client.conversations.get("conversation_id") for event in conversation.iter_messages(): # or: async for ... in aiter_messages() print(event.event, event.data) ``` ```python Python REST theme={null} import json import requests base = "https://backend.noxus.ai" cid = "conversation_id" with requests.get( f"{base}/v1/conversations/{cid}/events?format=json", headers={"X-API-KEY": "your_api_key"}, stream=True, ) as resp: for line in resp.iter_lines(): if line and line.startswith(b"data:"): print(json.loads(line[len(b"data:"):])) ``` ```javascript Node theme={null} const base = "https://backend.noxus.ai"; const cid = "conversation_id"; const resp = await fetch(`${base}/v1/conversations/${cid}/events?format=json`, { headers: { "X-API-KEY": "your_api_key" }, }); const reader = resp.body.getReader(); const decoder = new TextDecoder(); while (true) { const { done, value } = await reader.read(); if (done) break; process.stdout.write(decoder.decode(value)); } ``` ```bash cURL theme={null} #!/bin/bash base="https://backend.noxus.ai" cid="conversation_id" curl -N "$base/v1/conversations/$cid/events?format=json" \ -H "X-API-KEY: your_api_key" ``` ## Send a message with file attachments Attach files to a message via its `files` array. Each file needs a `name` plus **either** a public `url` **or** base64 `b64_content` (set `type` to the MIME type). A `url` is fetched server-side: it must be publicly reachable (private, loopback, and cloud-metadata addresses are rejected) and downloads are capped at 25 MB. This works with `chat` and with `stream` alike — the agent can read the attachment as part of the turn. ```python Python SDK theme={null} from noxus_sdk.client import Client from noxus_sdk.resources.conversations import MessageRequest, ConversationFile client = Client(api_key="your_api_key") conversation = client.conversations.create("My Conversation", agent_id="agent_id") message = MessageRequest( content="Summarise this document", files=[ConversationFile(name="report.pdf", url="https://example.com/report.pdf")], # Or inline base64: # files=[ConversationFile(name="report.pdf", b64_content="", type="application/pdf")], ) print(conversation.chat(message).parts) ``` ```python Python REST theme={null} import requests base = "https://backend.noxus.ai" headers = {"X-API-KEY": "your_api_key", "Content-Type": "application/json"} cid = "" # from the create step body = { "content": "Summarise this document", "files": [ { "status": "success", "name": "report.pdf", "url": "https://example.com/report.pdf", "type": "application/pdf", } ], } reply = requests.post( f"{base}/v1/conversations/{cid}/chat?assistant_id=agent_id", json=body, headers=headers, ).json() print(reply["parts"]) ``` ```bash cURL theme={null} #!/bin/bash base="https://backend.noxus.ai" cid="" # from the create step curl -X POST "$base/v1/conversations/$cid/chat?assistant_id=agent_id" \ -H "X-API-KEY: your_api_key" -H "Content-Type: application/json" \ -d '{ "content": "Summarise this document", "files": [ {"status": "success", "name": "report.pdf", "url": "https://example.com/report.pdf", "type": "application/pdf"} ] }' ``` Use a public `url` for files already hosted somewhere (fetched server-side, max 25 MB); use `b64_content` to inline a local file or one behind auth. The agent needs an enabled file-capable tool (e.g. file attachment / code execution) to act on attachments. Chat flows use the same conversation endpoints — create the conversation with `settings.agent_flow_id` set to the chat flow's id instead of passing `assistant_id`, then chat / stream exactly as above. # Knowledge bases: upload & search Source: https://docs.noxus.ai/api-reference/cookbook/knowledge-bases Upload documents to a knowledge base, wait for ingestion, then search Knowledge bases ingest documents asynchronously (parse → chunk → embed) and let you search the result: * **Upload** — `POST /v1/knowledge-bases/{kb_id}/upload_train` (multipart). Returns run ids you can poll. * **Search** — `POST /v1/knowledge-bases/{kb_id}/search`. Replace `kb_id` and `your_api_key`. Use the `prefix` parameter to organise and scope documents into folders. ## Upload documents and wait for ingestion Upload returns immediately with run ids; ingestion runs in the background. Poll the training runs (SDK `get_runs`, or the [running jobs](/api-reference/v1/knowledge-base/running-jobs) endpoint) until they complete before searching. Don't set `Content-Type` yourself on the upload — let your HTTP client set the multipart boundary. ```python Python SDK theme={null} # pip install noxus-sdk from pathlib import Path import time from noxus_sdk.client import Client client = Client(api_key="your_api_key") kb = client.knowledge_bases.get(knowledge_base_id="kb_id") run_ids = kb.upload_document(files=[Path("path/to/document.pdf")], prefix="/") print(f"ingestion started: {run_ids}") # Wait for ingestion to finish while any( r.status not in ("completed", "failed") for r in kb.get_runs(run_ids=",".join(run_ids)) ): time.sleep(3) print("ingestion done") ``` ```python Python REST theme={null} import requests base = "https://backend.noxus.ai" files = [("files", ("document.txt", open("path/to/document.txt", "rb"), "text/plain"))] resp = requests.post( f"{base}/v1/knowledge-bases/kb_id/upload_train", headers={"X-API-KEY": "your_api_key"}, # no Content-Type — requests sets multipart files=files, params={"prefix": "/"}, ) print(resp.json()) # run ids to poll for ingestion status ``` ```javascript Node theme={null} import fs from "fs"; const base = "https://backend.noxus.ai"; const form = new FormData(); form.append("files", new Blob([fs.readFileSync("path/to/document.txt")]), "document.txt"); const resp = await fetch(`${base}/v1/knowledge-bases/kb_id/upload_train?prefix=/`, { method: "POST", headers: { "X-API-KEY": "your_api_key" }, // fetch sets the multipart boundary body: form, }); console.log(await resp.json()); // run ids ``` ```bash cURL theme={null} curl -X POST "https://backend.noxus.ai/v1/knowledge-bases/kb_id/upload_train?prefix=/" \ -H "X-API-KEY: your_api_key" \ -F "files=@path/to/document.txt" ``` ## Search the knowledge base Runs a semantic / hybrid query (depending on the KB's retrieval settings) over the ingested content and returns the matching chunks with their source documents. Scope the search with `prefix`. ```python Python SDK theme={null} from noxus_sdk.client import Client client = Client(api_key="your_api_key") kb = client.knowledge_bases.get(knowledge_base_id="kb_id") for result in kb.search(query="Your search query", prefix="/"): print(result.document_source.name) print(result.content) ``` ```python Python REST theme={null} import requests resp = requests.post( "https://backend.noxus.ai/v1/knowledge-bases/kb_id/search", params={"query": "Your search query", "prefix": "/"}, headers={"X-API-KEY": "your_api_key", "Content-Type": "application/json"}, ) for result in resp.json(): print(result["document_source"]["name"]) print(result["content"]) ``` ```javascript Node theme={null} const params = new URLSearchParams({ query: "Your search query", prefix: "/" }); const resp = await fetch( `https://backend.noxus.ai/v1/knowledge-bases/kb_id/search?${params}`, { method: "POST", headers: { "X-API-KEY": "your_api_key", "Content-Type": "application/json" }, } ); for (const result of await resp.json()) { console.log(result.document_source.name); console.log(result.content); } ``` ```bash cURL theme={null} curl -X POST "https://backend.noxus.ai/v1/knowledge-bases/kb_id/search?query=Your%20search%20query&prefix=/" \ -H "X-API-KEY: your_api_key" \ -H "Content-Type: application/json" ``` ## Async / await with the SDK Use the `a`-prefixed coroutines (`aget`, `aupload_document`, `aget_runs`, `asearch`) from async code so the event loop stays responsive. ```python Python SDK theme={null} import asyncio from pathlib import Path from noxus_sdk.client import Client async def main(): client = Client(api_key="your_api_key") kb = await client.knowledge_bases.aget(knowledge_base_id="kb_id") await kb.aupload_document(files=[Path("path/to/document.pdf")], prefix="/") results = await kb.asearch(query="Your search query", prefix="/") for r in results: print(r.document_source.name, r.content) asyncio.run(main()) ``` # API Cookbook Source: https://docs.noxus.ai/api-reference/cookbook/overview Copy-paste recipes for the most common API tasks, in every language Task-oriented examples for the Noxus API and SDK. Each recipe shows the **same operation in four flavours** — Python SDK, Python REST (`requests`), Node (`fetch`/`axios`), and cURL — so you can drop it straight into your stack. Create runs and get results — wait (sync), poll, or stream over SSE. Chat with an agent, stream replies token-by-token, and tail live events. Upload and ingest documents, then search them. ## Before you start Create one under **Workspace control → API keys**. All requests authenticate with the `X-API-KEY` header. The managed cloud is `https://backend.noxus.ai`. Self-hosted deployments use their own host — the Python SDK reads `NOXUS_BACKEND_URL` or accepts `base_url=` on the `Client`. `pip install noxus-sdk` for the Python examples. The REST/Node/cURL recipes have no dependency beyond an HTTP client. Never commit API keys. Load them from an environment variable or a secret manager. The examples use `your_api_key` as a placeholder. ## Which execution pattern should I use? | Pattern | Use it when | Avoid it when | | ---------------- | ------------------------------------------------------------ | -------------------------------------------------------------- | | **Wait (sync)** | Short, interactive runs where you want one response | Long-running or highly parallel work — it ties up a connection | | **Poll** | Long runs, or many runs in parallel; you control the cadence | You need sub-second latency on completion | | **Stream (SSE)** | Live UIs that show node-by-node / token-by-token progress | Simple server-to-server calls where you only need the result | # Workflows: run & get results Source: https://docs.noxus.ai/api-reference/cookbook/workflows Create workflow runs and collect their output — wait, poll, or stream Every recipe below targets the same workflow run lifecycle: * **Async create** — `POST /v1/workflows/{workflow_id}/runs` returns immediately with a run id. * **Sync create** — `POST /v1/workflows/{workflow_id}/runs/sync` blocks server-side and returns the output. * **Poll** — `GET /v1/workflows/{workflow_id}/runs/{run_id}`. * **Stream** — `GET /v1/runs/{run_id}/events` (Server-Sent Events). Replace `workflow_id` and `your_api_key`. The body's `input` keys are your workflow's input-node labels (or IDs). ## Create a run and wait for the result Simplest option — blocks until the run finishes. Best for short, interactive flows. Avoid it for long-running or highly parallel workloads, where it ties up a connection for the whole run. ```python Python SDK theme={null} # pip install noxus-sdk from noxus_sdk.client import Client client = Client(api_key="your_api_key") workflow = client.workflows.get(workflow_id="workflow_id") run = workflow.run(body={"User Question": "What is machine learning?"}) result = run.wait(interval=5) # polls/streams under the hood until terminal print(result.output) ``` ```python Python REST theme={null} import requests resp = requests.post( "https://backend.noxus.ai/v1/workflows/workflow_id/runs/sync", json={"input": {"User Question": "What is machine learning?"}}, headers={"X-API-KEY": "your_api_key", "Content-Type": "application/json"}, ) resp.raise_for_status() print(resp.json()) # the run output ``` ```javascript Node theme={null} const resp = await fetch( "https://backend.noxus.ai/v1/workflows/workflow_id/runs/sync", { method: "POST", headers: { "X-API-KEY": "your_api_key", "Content-Type": "application/json" }, body: JSON.stringify({ input: { "User Question": "What is machine learning?" } }), } ); console.log(await resp.json()); ``` ```bash cURL theme={null} curl -X POST "https://backend.noxus.ai/v1/workflows/workflow_id/runs/sync" \ -H "X-API-KEY: your_api_key" \ -H "Content-Type: application/json" \ -d '{"input": {"User Question": "What is machine learning?"}}' ``` ## Create a run and poll Create asynchronously (returns a run id immediately), then check the run's status at your own cadence. The right default for long-running flows or when launching many runs in parallel — your process never blocks on a single run. ```python Python SDK theme={null} import time from noxus_sdk.client import Client client = Client(api_key="your_api_key") workflow = client.workflows.get(workflow_id="workflow_id") run = workflow.run(body={"User Question": "What is machine learning?"}) while run.refresh().status not in ("completed", "failed"): print(f"status={run.status} progress={run.progress}%") time.sleep(2) print(run.output) ``` ```python Python REST theme={null} import time import requests base = "https://backend.noxus.ai/v1/workflows/workflow_id" headers = {"X-API-KEY": "your_api_key", "Content-Type": "application/json"} # 1. Create the run run = requests.post( f"{base}/runs", json={"input": {"User Question": "What is machine learning?"}}, headers=headers, ).json() run_id = run["id"] # 2. Poll until terminal while True: run = requests.get(f"{base}/runs/{run_id}", headers=headers).json() if run["status"] in ("completed", "failed"): break time.sleep(2) print(run.get("output")) ``` ```javascript Node theme={null} const base = "https://backend.noxus.ai/v1/workflows/workflow_id"; const headers = { "X-API-KEY": "your_api_key", "Content-Type": "application/json" }; const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); // 1. Create the run const created = await fetch(`${base}/runs`, { method: "POST", headers, body: JSON.stringify({ input: { "User Question": "What is machine learning?" } }), }).then((r) => r.json()); // 2. Poll until terminal let run; do { await sleep(2000); run = await fetch(`${base}/runs/${created.id}`, { headers }).then((r) => r.json()); console.log(`status=${run.status} progress=${run.progress}%`); } while (!["completed", "failed"].includes(run.status)); console.log(run.output); ``` ```bash cURL theme={null} #!/bin/bash base="https://backend.noxus.ai/v1/workflows/workflow_id" # 1. Create the run run_id=$(curl -s -X POST "$base/runs" \ -H "X-API-KEY: your_api_key" -H "Content-Type: application/json" \ -d '{"input": {"User Question": "What is machine learning?"}}' | jq -r '.id') # 2. Poll until terminal while true; do run=$(curl -s "$base/runs/$run_id" -H "X-API-KEY: your_api_key") status=$(echo "$run" | jq -r '.status') echo "status=$status" [ "$status" = "completed" ] || [ "$status" = "failed" ] && break sleep 2 done echo "$run" | jq '.output' ``` ## Create a run and stream events (SSE) Streams progress over Server-Sent Events as each node finishes. Best for live UIs and long flows where you want incremental feedback instead of one final payload. Each event has a `type` and a `data` payload; the stream ends when the run reaches a terminal state. ```python Python SDK theme={null} from noxus_sdk.client import Client client = Client(api_key="your_api_key") workflow = client.workflows.get(workflow_id="workflow_id") # Creates the run and yields events until it reaches a terminal state for event in workflow.run_and_stream(body={"User Question": "What is machine learning?"}): print(event.type, event.data) ``` ```python Python REST theme={null} import json import requests base = "https://backend.noxus.ai" headers = {"X-API-KEY": "your_api_key", "Content-Type": "application/json"} run = requests.post( f"{base}/v1/workflows/workflow_id/runs", json={"input": {"User Question": "What is machine learning?"}}, headers=headers, ).json() with requests.get( f"{base}/v1/runs/{run['id']}/events", headers={"X-API-KEY": "your_api_key"}, stream=True, ) as resp: for line in resp.iter_lines(): if line and line.startswith(b"data:"): event = json.loads(line[len(b"data:"):]) print(event.get("type"), event.get("data")) ``` ```javascript Node theme={null} const base = "https://backend.noxus.ai"; const created = await fetch(`${base}/v1/workflows/workflow_id/runs`, { method: "POST", headers: { "X-API-KEY": "your_api_key", "Content-Type": "application/json" }, body: JSON.stringify({ input: { "User Question": "What is machine learning?" } }), }).then((r) => r.json()); const resp = await fetch(`${base}/v1/runs/${created.id}/events`, { headers: { "X-API-KEY": "your_api_key" }, }); const reader = resp.body.getReader(); const decoder = new TextDecoder(); while (true) { const { done, value } = await reader.read(); if (done) break; process.stdout.write(decoder.decode(value)); // raw SSE frames } ``` ```bash cURL theme={null} #!/bin/bash base="https://backend.noxus.ai" run_id=$(curl -s -X POST "$base/v1/workflows/workflow_id/runs" \ -H "X-API-KEY: your_api_key" -H "Content-Type: application/json" \ -d '{"input": {"User Question": "What is machine learning?"}}' | jq -r '.id') # -N disables curl's output buffering so events arrive live curl -N "$base/v1/runs/$run_id/events" -H "X-API-KEY: your_api_key" ``` ## Async / await with the SDK Every SDK method has an `a`-prefixed coroutine variant (`aget`, `arun`, `a_wait`, `arefresh`, `astream`, `arun_and_stream`). Use them inside an async application so the event loop is never blocked. ```python Python SDK theme={null} import asyncio from noxus_sdk.client import Client async def main(): client = Client(api_key="your_api_key") workflow = await client.workflows.aget(workflow_id="workflow_id") run = await workflow.arun(body={"User Question": "What is machine learning?"}) # Wait for the result... result = await run.a_wait(interval=5) print(result.output) # ...or stream events instead: # async for event in run.astream(): # print(event.type, event.data) asyncio.run(main()) ``` ## Sending files as inputs If a workflow has a **File**, **Image**, or **Audio** input node, pass that input as an object with a `uri` (and ideally a `name`) instead of a string. Three shapes are accepted: * **Public URL** — `{"uri": "https://…", "name": "…"}`. Noxus downloads it server-side and stores a copy. The URL must be publicly reachable; private, loopback, and cloud-metadata addresses are rejected, and downloads are capped at 25 MB. * **Base64 data URI** — `{"uri": "data:;base64,", "name": "…"}`. Noxus decodes and stores it. Best for files you can't expose over a URL. * **Existing Noxus file** — `{"uri": "spot://", "name": "…"}`, where the id comes from a prior `POST /v1/file` upload. The key is the input node's label (here, `"Document"`); everything else in the body works exactly like the recipes above (wait, poll, or stream). ```python Python SDK theme={null} from noxus_sdk.client import Client client = Client(api_key="your_api_key") workflow = client.workflows.get(workflow_id="workflow_id") # Public URL run = workflow.run(body={ "Document": {"uri": "https://example.com/report.pdf", "name": "report.pdf"}, }) # …or base64 (e.g. a local file) import base64 with open("report.pdf", "rb") as f: data = base64.b64encode(f.read()).decode() run = workflow.run(body={ "Document": {"uri": f"data:application/pdf;base64,{data}", "name": "report.pdf"}, }) print(run.wait().output) ``` ```python Python REST theme={null} import base64 import requests headers = {"X-API-KEY": "your_api_key", "Content-Type": "application/json"} # Public URL requests.post( "https://backend.noxus.ai/v1/workflows/workflow_id/runs/sync", json={"input": {"Document": {"uri": "https://example.com/report.pdf", "name": "report.pdf"}}}, headers=headers, ) # …or base64 with open("report.pdf", "rb") as f: data = base64.b64encode(f.read()).decode() requests.post( "https://backend.noxus.ai/v1/workflows/workflow_id/runs/sync", json={"input": {"Document": {"uri": f"data:application/pdf;base64,{data}", "name": "report.pdf"}}}, headers=headers, ) ``` ```bash cURL theme={null} # Public URL curl -X POST "https://backend.noxus.ai/v1/workflows/workflow_id/runs/sync" \ -H "X-API-KEY: your_api_key" -H "Content-Type: application/json" \ -d '{"input": {"Document": {"uri": "https://example.com/report.pdf", "name": "report.pdf"}}}' # Base64 (build the data URI from a local file) data=$(base64 -w0 report.pdf) curl -X POST "https://backend.noxus.ai/v1/workflows/workflow_id/runs/sync" \ -H "X-API-KEY: your_api_key" -H "Content-Type: application/json" \ -d "{\"input\": {\"Document\": {\"uri\": \"data:application/pdf;base64,$data\", \"name\": \"report.pdf\"}}}" ``` Outputs that produce files come back as `{"text": ..., "file": {...}}` objects on the relevant output key — read the `file` metadata (including its URL) from the run output. ## Webhooks (fire-and-forget) Pass a `callback_url` when creating a run and Noxus will `POST` the result to it when the run reaches a terminal state — no polling or streaming needed. ```python Python SDK theme={null} run = workflow.run( body={"User Question": "What is machine learning?"}, callback_url="https://your-app.example.com/noxus/webhook", ) ``` ```bash cURL theme={null} curl -X POST "https://backend.noxus.ai/v1/workflows/workflow_id/runs" \ -H "X-API-KEY: your_api_key" -H "Content-Type: application/json" \ -d '{ "input": {"User Question": "What is machine learning?"}, "callback_url": "https://your-app.example.com/noxus/webhook" }' ``` See [Create Async Run](/api-reference/v1/runs/create-run-with-api-key) for the full webhook payload, retry, and timeout behaviour. # API Reference Source: https://docs.noxus.ai/api-reference/introduction API Authentication and main concepts The API of Noxus exposes a set of operations for authenticated clients to interact with core entities in the platform, particularly: * Workflows * Runs * Knowledge Bases Before interacting with any of the concepts it's however necessary to understand the access mechanism and the organizational structure of the platform. ### Organizational Structure in Noxus In the Noxus platform, any resource within an organization is associated with an workspace. Any action executed through the API will run in the context of a specific workspace. 1. **Workspaces**: An organization is divided into multiple **workspaces**, each functioning as an independent area to manage flows, runs, and other resources. Workspaces help segregate projects, teams, or environments (e.g., development, staging, production). 2. **Members and Roles**: Members are assigned to workspaces, where they have specific roles that define their permissions and access levels. This role-based system ensures secure and streamlined collaboration. 3. **Personal Workspaces**: Every member has a **personal workspace**, providing a private space for individual tasks and experimentation. Members can also join and collaborate in other workspaces within the organization. ### Authentication Authentication in the Noxus platform is performed using an API key. To generate an API key, navigate to **Settings > Organization > Workspaces > API Keys** in the dashboard. An API key is always associated with one workspace, with full permissions. Be careful when sharing access to an API key, as it will grant access to all the resources in the workspace with full permissions. Include the API key in your requests by adding it to the header as follows: ```json theme={null} X-API-KEY: ``` # Public Create Api Key Source: https://docs.noxus.ai/api-reference/v1--admin/public-create-api-key post /v1/admin/groups/{group_id}/api-keys # Public Create Group Source: https://docs.noxus.ai/api-reference/v1--admin/public-create-group post /v1/admin/groups # Public Delete Group Source: https://docs.noxus.ai/api-reference/v1--admin/public-delete-group delete /v1/admin/groups/{group_id} # Public Get Me Source: https://docs.noxus.ai/api-reference/v1--admin/public-get-me get /v1/admin/me # Public List Groups Source: https://docs.noxus.ai/api-reference/v1--admin/public-list-groups get /v1/admin/groups # Add Agent Source: https://docs.noxus.ai/api-reference/v1--agents-co-workers/add-agent post /v1/agents # Delete Agent Source: https://docs.noxus.ai/api-reference/v1--agents-co-workers/delete-agent delete /v1/agents/{agent_id} # Duplicate Agent Source: https://docs.noxus.ai/api-reference/v1--agents-co-workers/duplicate-agent post /v1/agents/{agent_id}/duplicate # Get Agent Source: https://docs.noxus.ai/api-reference/v1--agents-co-workers/get-agent get /v1/agents/{agent_id} # Get Agent Versions Source: https://docs.noxus.ai/api-reference/v1--agents-co-workers/get-agent-versions get /v1/agents/{agent_id}/versions # Get Agents Source: https://docs.noxus.ai/api-reference/v1--agents-co-workers/get-agents get /v1/agents # Get Tool Schemas Source: https://docs.noxus.ai/api-reference/v1--agents-co-workers/get-tool-schemas get /v1/agents/tool-schemas Return NCL config schemas for all tool types, grouped by category. # Public Export Agent Source: https://docs.noxus.ai/api-reference/v1--agents-co-workers/public-export-agent post /v1/agents/{agent_id}/export # Public Export Agent Preview Source: https://docs.noxus.ai/api-reference/v1--agents-co-workers/public-export-agent-preview get /v1/agents/{agent_id}/export/preview # Public Import Agent Source: https://docs.noxus.ai/api-reference/v1--agents-co-workers/public-import-agent post /v1/agents/import # Publish Assistant Source: https://docs.noxus.ai/api-reference/v1--agents-co-workers/publish-assistant post /v1/agents/{agent_id}/publish # Restore Agent Source: https://docs.noxus.ai/api-reference/v1--agents-co-workers/restore-agent post /v1/agents/{agent_id}/restore # Update Agent Source: https://docs.noxus.ai/api-reference/v1--agents-co-workers/update-agent patch /v1/agents/{agent_id} # Get workspace analytics Source: https://docs.noxus.ai/api-reference/v1--analytics/get-workspace-analytics get /v1/analytics/{metric} Query analytics metrics for the workspace associated with the API key. Results are scoped to the workspace and time range provided. # Add Message Source: https://docs.noxus.ai/api-reference/v1--conversations/add-message post /v1/conversations/{conversation_id} # Chat Source: https://docs.noxus.ai/api-reference/v1--conversations/chat post /v1/conversations/{conversation_id}/chat # Conversation Events Source: https://docs.noxus.ai/api-reference/v1--conversations/conversation-events get /v1/conversations/{conversation_id}/events Tail the SSE event stream of an in-progress conversation run. Reads the same `vercel::{conversation_id}` Redis stream that the worker publishes to (and that `/stream` consumes). Use `?etag=` to resume from a specific Redis stream id; use `?format=json` for normalised envelope frames (`event: message\ndata: {"event": ..., "data": ...}`). # Create Conversation Source: https://docs.noxus.ai/api-reference/v1--conversations/create-conversation post /v1/conversations # Delete Conversation Source: https://docs.noxus.ai/api-reference/v1--conversations/delete-conversation delete /v1/conversations/{conversation_id} # Get Conversation Source: https://docs.noxus.ai/api-reference/v1--conversations/get-conversation get /v1/conversations/{conversation_id} # List Conversations Source: https://docs.noxus.ai/api-reference/v1--conversations/list-conversations get /v1/conversations # Refresh Message Source: https://docs.noxus.ai/api-reference/v1--conversations/refresh-message post /v1/conversations/{conversation_id}/messages/{message_id}/refresh # Stream Message Source: https://docs.noxus.ai/api-reference/v1--conversations/stream-message post /v1/conversations/{conversation_id}/stream Send a message and stream agent SSE events back over the same request. Unlike `POST /v1/conversations/{id}` (which blocks until the run finishes), this endpoint dispatches the run and immediately returns a streaming SSE response carrying every event the worker emits to Redis. `format` controls the SSE wire format: * `vercel` (default) — pydantic-ai Vercel AI SDK frames passed through verbatim. * `json` — each frame normalised to `event: message\ndata: {"event": ..., "data": ...}\n\n`, terminated by `event: done`. # Update conversation Source: https://docs.noxus.ai/api-reference/v1--conversations/update-conversation patch /v1/conversations/{conversation_id} # Activate Deployment Source: https://docs.noxus.ai/api-reference/v1--deployments/activate-deployment post /v1/agents/{assistant_id}/deployments/{deployment_id}/activate # Create Deployment Source: https://docs.noxus.ai/api-reference/v1--deployments/create-deployment post /v1/agents/{assistant_id}/deployments # Deactivate Deployment Source: https://docs.noxus.ai/api-reference/v1--deployments/deactivate-deployment post /v1/agents/{assistant_id}/deployments/{deployment_id}/deactivate # Delete Deployment Source: https://docs.noxus.ai/api-reference/v1--deployments/delete-deployment delete /v1/agents/{assistant_id}/deployments/{deployment_id} # Get Deployment Source: https://docs.noxus.ai/api-reference/v1--deployments/get-deployment get /v1/agents/{assistant_id}/deployments/{deployment_id} # Get Deployment Events Source: https://docs.noxus.ai/api-reference/v1--deployments/get-deployment-events get /v1/agents/{assistant_id}/deployments/{deployment_id}/events # List Channels Source: https://docs.noxus.ai/api-reference/v1--deployments/list-channels get /v1/channels Metadata for every registered deployment channel (respects kill switch). # List Deployments Source: https://docs.noxus.ai/api-reference/v1--deployments/list-deployments get /v1/agents/{assistant_id}/deployments # Update Deployment Source: https://docs.noxus.ai/api-reference/v1--deployments/update-deployment patch /v1/agents/{assistant_id}/deployments/{deployment_id} # Receive Email Source: https://docs.noxus.ai/api-reference/v1--inboxes/receive-email post /v1/inbox/webhook # Bootstrap Status Source: https://docs.noxus.ai/api-reference/v1--insights/bootstrap-status get /v1/agents/{assistant_id}/insights/bootstrap # Conversation Funnel Source: https://docs.noxus.ai/api-reference/v1--insights/conversation-funnel get /v1/agents/{assistant_id}/insights/conversation-funnel # Csat Score Source: https://docs.noxus.ai/api-reference/v1--insights/csat-score get /v1/agents/{assistant_id}/insights/csat-score # Custom Insights Source: https://docs.noxus.ai/api-reference/v1--insights/custom-insights get /v1/agents/{assistant_id}/insights/custom-insights # Insight Conversations Source: https://docs.noxus.ai/api-reference/v1--insights/insight-conversations get /v1/agents/{assistant_id}/insights/conversations # Noticed Source: https://docs.noxus.ai/api-reference/v1--insights/noticed get /v1/agents/{assistant_id}/insights/noticed # Rating Drivers Source: https://docs.noxus.ai/api-reference/v1--insights/rating-drivers get /v1/agents/{assistant_id}/insights/rating-drivers # Sentiment Over Time Source: https://docs.noxus.ai/api-reference/v1--insights/sentiment-over-time get /v1/agents/{assistant_id}/insights/sentiment-over-time # Sub Topics Source: https://docs.noxus.ai/api-reference/v1--insights/sub-topics get /v1/agents/{assistant_id}/insights/sub-topics # Top Topics Source: https://docs.noxus.ai/api-reference/v1--insights/top-topics get /v1/agents/{assistant_id}/insights/top-topics # Add Knowledge Base Document Source: https://docs.noxus.ai/api-reference/v1--knowledge-bases/add-knowledge-base-document post /v1/knowledge-bases/{knowledge_base_id}/document # Add Knowledge Base V2 Source: https://docs.noxus.ai/api-reference/v1--knowledge-bases/add-knowledge-base-v2 post /v1/knowledge-bases # Delete Knowledge Base Document Source: https://docs.noxus.ai/api-reference/v1--knowledge-bases/delete-knowledge-base-document delete /v1/knowledge-bases/{knowledge_base_id}/document/{document_id} # Delete Knowledge Base V2 Source: https://docs.noxus.ai/api-reference/v1--knowledge-bases/delete-knowledge-base-v2 delete /v1/knowledge-bases/{knowledge_base_id} # Dismiss Knowledge Base Document Source: https://docs.noxus.ai/api-reference/v1--knowledge-bases/dismiss-knowledge-base-document patch /v1/knowledge-bases/{knowledge_base_id}/document/{document_id}/dismiss Mark a document as dismissed (typically used for error documents) # Fetch Knowledge Base Custom Types Source: https://docs.noxus.ai/api-reference/v1--knowledge-bases/fetch-knowledge-base-custom-types get /v1/knowledge-bases/types # Generic Train Knowledge Base Source: https://docs.noxus.ai/api-reference/v1--knowledge-bases/generic-train-knowledge-base post /v1/knowledge-bases/{knowledge_base_id}/generic_train # Get Compatible Mime Types Source: https://docs.noxus.ai/api-reference/v1--knowledge-bases/get-compatible-mime-types get /v1/knowledge-bases/mime-types Get supported MIME types for a specific entity type # Get Knowledge Base Document Source: https://docs.noxus.ai/api-reference/v1--knowledge-bases/get-knowledge-base-document get /v1/knowledge-bases/{knowledge_base_id}/document/{document_id} # Get Knowledge Base Documents Source: https://docs.noxus.ai/api-reference/v1--knowledge-bases/get-knowledge-base-documents get /v1/knowledge-bases/{knowledge_base_id}/documents/{status} # Get Knowledge Base Ingestion Documents Source: https://docs.noxus.ai/api-reference/v1--knowledge-bases/get-knowledge-base-ingestion-documents get /v1/knowledge-bases/{knowledge_base_id}/documents/ingestion Get documents that are currently being ingested (uploaded, training, or error status) # Get Knowledge Base Running Jobs Source: https://docs.noxus.ai/api-reference/v1--knowledge-bases/get-knowledge-base-running-jobs get /v1/knowledge-bases/{knowledge_base_id}/runs # Get Knowledge Base Tree Source: https://docs.noxus.ai/api-reference/v1--knowledge-bases/get-knowledge-base-tree get /v1/knowledge-bases/{knowledge_base_id}/tree Get a hierarchical tree view of files and folders in a knowledge base. # Get Knowledge Base V2 Source: https://docs.noxus.ai/api-reference/v1--knowledge-bases/get-knowledge-base-v2 get /v1/knowledge-bases/{knowledge_base_id} # Get Knowledge Bases V2 Source: https://docs.noxus.ai/api-reference/v1--knowledge-bases/get-knowledge-bases-v2 get /v1/knowledge-bases # List Knowledge Base Folder Source: https://docs.noxus.ai/api-reference/v1--knowledge-bases/list-knowledge-base-folder get /v1/knowledge-bases/{knowledge_base_id}/ls List files and folders in a specific folder of a knowledge base (non-recursive). # Public Export Kb Source: https://docs.noxus.ai/api-reference/v1--knowledge-bases/public-export-kb post /v1/knowledge-bases/{knowledge_base_id}/export Export a knowledge base. Returns an export file (v4 plaintext YAML by default, v3 base64 on request) that can be imported later. Note: KBs larger than 15MB are exported without their documents (metadata and settings only); the response is ``206 Partial Content`` and carries an ``X-Export-Warning`` header. # Public Import Kb Source: https://docs.noxus.ai/api-reference/v1--knowledge-bases/public-import-kb post /v1/knowledge-bases/import Import a knowledge base from an export file. The definition should be the base64-encoded content from a previous export. Returns the created knowledge base with its new ID. # Retry Document Ingestion Source: https://docs.noxus.ai/api-reference/v1--knowledge-bases/retry-document-ingestion post /v1/knowledge-bases/{knowledge_base_id}/document/{document_id}/retry # Train Knowledge Base Source: https://docs.noxus.ai/api-reference/v1--knowledge-bases/retry-train-knowledge-base post /v1/knowledge-bases/{knowledge_base_id}/retry_all # Search Knowledge Base Source: https://docs.noxus.ai/api-reference/v1--knowledge-bases/search-knowledge-base post /v1/knowledge-bases/{knowledge_base_id}/search # Search Knowledge Base Documents Source: https://docs.noxus.ai/api-reference/v1--knowledge-bases/search-knowledge-base-documents get /v1/knowledge-bases/{knowledge_base_id}/documents/search Search documents by file name using fuzzy matching. # Update Knowledge Base Document Source: https://docs.noxus.ai/api-reference/v1--knowledge-bases/update-knowledge-base-document patch /v1/knowledge-bases/{knowledge_base_id}/document/{document_id} # Update Knowledge Base V2 Source: https://docs.noxus.ai/api-reference/v1--knowledge-bases/update-knowledge-base-v2 patch /v1/knowledge-bases/{knowledge_base_id} # Upload Train Knowledge Base Source: https://docs.noxus.ai/api-reference/v1--knowledge-bases/upload-train-knowledge-base post /v1/knowledge-bases/{knowledge_base_id}/upload_train # Get Llms Source: https://docs.noxus.ai/api-reference/v1--platform/get-llms get /v1/models/llms # Get Llms Presets Source: https://docs.noxus.ai/api-reference/v1--platform/get-llms-presets get /v1/models/llms/presets # Get Nodes Source: https://docs.noxus.ai/api-reference/v1--platform/get-nodes get /v1/nodes # Create Role Source: https://docs.noxus.ai/api-reference/v1--roles/create-role post /v1/admin/roles Create a tenant role. # Delete Role Source: https://docs.noxus.ai/api-reference/v1--roles/delete-role delete /v1/admin/roles/{role_id} Delete a tenant role (system roles cannot be deleted). # List Roles Source: https://docs.noxus.ai/api-reference/v1--roles/list-roles get /v1/admin/roles List the tenant's roles. Requires a system key with ``org:admin``. # Create Run With Api Key Source: https://docs.noxus.ai/api-reference/v1--runs/create-run-with-api-key post /v1/workflows/{workflow_id}/runs # Create Sync Run Source: https://docs.noxus.ai/api-reference/v1--runs/create-sync-run post /v1/workflows/{workflow_id}/runs/sync # Get Runs Public Source: https://docs.noxus.ai/api-reference/v1--runs/get-runs-public get /v1/workflows/{workflow_id}/runs # Public Get Paginated Runs Source: https://docs.noxus.ai/api-reference/v1--runs/public-get-paginated-runs get /v1/runs # Public Get Run By Id Source: https://docs.noxus.ai/api-reference/v1--runs/public-get-run-by-id get /v1/runs/{run_id} # Public Get Run By Id And Workflow Source: https://docs.noxus.ai/api-reference/v1--runs/public-get-run-by-id-and-workflow get /v1/workflows/{workflow_id}/runs/{run_id} # Public Get Run Data By Id Source: https://docs.noxus.ai/api-reference/v1--runs/public-get-run-data-by-id get /v1/runs/{run_id}/data # Public Get Run Node Io Source: https://docs.noxus.ai/api-reference/v1--runs/public-get-run-node-io get /v1/runs/{run_id}/io/{node_id} Inputs/outputs of a single node within a run (with secret redaction). ``it`` is the node's iteration (1-based) for nodes that fan out in a loop; 0 is tolerated and treated as the first iteration. # Public Run Events Source: https://docs.noxus.ai/api-reference/v1--runs/public-run-events get /v1/runs/{run_id}/events # Public Stop Run Source: https://docs.noxus.ai/api-reference/v1--runs/public-stop-run post /v1/runs/{run_id}/stop # Search Runs Public Source: https://docs.noxus.ai/api-reference/v1--runs/search-runs-public post /v1/runs/search # Create Sandbox Source: https://docs.noxus.ai/api-reference/v1--sandboxes/create-sandbox post /v1/sandboxes Create a sandbox owned by this workspace. # Delete Sandbox Source: https://docs.noxus.ai/api-reference/v1--sandboxes/delete-sandbox delete /v1/sandboxes/{sandbox_id} Destroy a sandbox and its filesystem. # Get Sandbox Source: https://docs.noxus.ai/api-reference/v1--sandboxes/get-sandbox get /v1/sandboxes/{sandbox_id} # List Sandboxes Source: https://docs.noxus.ai/api-reference/v1--sandboxes/list-sandboxes get /v1/sandboxes List this workspace's live sandboxes. # Read File Source: https://docs.noxus.ai/api-reference/v1--sandboxes/read-file get /v1/sandboxes/{sandbox_id}/files Read a file out of the sandbox. Binary files should be fetched with ``encoding=base64``; utf-8 reads of non-text content are rejected rather than silently mangled. # Run Command Source: https://docs.noxus.ai/api-reference/v1--sandboxes/run-command post /v1/sandboxes/{sandbox_id}/commands Run a command and return its stdout, stderr and exit code. # Write File Source: https://docs.noxus.ai/api-reference/v1--sandboxes/write-file post /v1/sandboxes/{sandbox_id}/files Write a file into the sandbox. # Create System Key Source: https://docs.noxus.ai/api-reference/v1--system-keys/create-system-key post /v1/admin/system-keys Mint a tenant-scoped system key in the tenant's admin workspace. # Delete System Key Source: https://docs.noxus.ai/api-reference/v1--system-keys/delete-system-key delete /v1/admin/system-keys/{key_id} Revoke a system key. # List System Keys Source: https://docs.noxus.ai/api-reference/v1--system-keys/list-system-keys get /v1/admin/system-keys List the tenant's system keys. # List Tenant Users Source: https://docs.noxus.ai/api-reference/v1--system-keys/list-tenant-users get /v1/admin/users List the tenant's users. Requires a system key with ``users:read``. # Add Column Source: https://docs.noxus.ai/api-reference/v1--tables/add-column post /v1/tables/{table_id}/columns # Change Column Type Source: https://docs.noxus.ai/api-reference/v1--tables/change-column-type patch /v1/tables/{table_id}/columns/{name}/type # Clear Rows Source: https://docs.noxus.ai/api-reference/v1--tables/clear-rows delete /v1/tables/{table_id}/rows # Create Table Source: https://docs.noxus.ai/api-reference/v1--tables/create-table post /v1/tables # Delete Row Source: https://docs.noxus.ai/api-reference/v1--tables/delete-row delete /v1/tables/{table_id}/rows/{row_id} # Delete Table Source: https://docs.noxus.ai/api-reference/v1--tables/delete-table delete /v1/tables/{table_id} # Drop Column Source: https://docs.noxus.ai/api-reference/v1--tables/drop-column delete /v1/tables/{table_id}/columns/{name} # Export Table Csv Source: https://docs.noxus.ai/api-reference/v1--tables/export-table-csv get /v1/tables/{table_id}/export Stream a table's rows as CSV (capped at 10k rows). # Get Table Source: https://docs.noxus.ai/api-reference/v1--tables/get-table get /v1/tables/{table_id} # Get Table Stats Source: https://docs.noxus.ai/api-reference/v1--tables/get-table-stats get /v1/tables/{table_id}/stats # Import Csv Source: https://docs.noxus.ai/api-reference/v1--tables/import-csv post /v1/tables/import Create a table from a CSV upload. ``spec`` is a JSON ImportCsvSpec (name, id_type, has_header, columns). # Insert Row Source: https://docs.noxus.ai/api-reference/v1--tables/insert-row post /v1/tables/{table_id}/rows # Insert Rows Source: https://docs.noxus.ai/api-reference/v1--tables/insert-rows post /v1/tables/{table_id}/rows/bulk Insert many rows in one fast load. For thousands of rows use CSV import. # List Rows Source: https://docs.noxus.ai/api-reference/v1--tables/list-rows get /v1/tables/{table_id}/rows # List Tables Source: https://docs.noxus.ai/api-reference/v1--tables/list-tables get /v1/tables # Query Tables Source: https://docs.noxus.ai/api-reference/v1--tables/query-tables post /v1/tables/query Run a read-only SQL query across this workspace's tables. # Rename Column Source: https://docs.noxus.ai/api-reference/v1--tables/rename-column patch /v1/tables/{table_id}/columns/{name}/rename # Reorder Columns Source: https://docs.noxus.ai/api-reference/v1--tables/reorder-columns patch /v1/tables/{table_id}/columns/reorder # Update Column Label Source: https://docs.noxus.ai/api-reference/v1--tables/update-column-label patch /v1/tables/{table_id}/columns/{name}/label # Update Row Source: https://docs.noxus.ai/api-reference/v1--tables/update-row patch /v1/tables/{table_id}/rows/{row_id} # Update Table Source: https://docs.noxus.ai/api-reference/v1--tables/update-table patch /v1/tables/{table_id} # Create Workflow Trigger Source: https://docs.noxus.ai/api-reference/v1--triggers/create-workflow-trigger post /v1/workflows/{workflow_id}/triggers Create a trigger on a workflow. The trigger is attributed to this API key. # Delete Trigger Source: https://docs.noxus.ai/api-reference/v1--triggers/delete-trigger delete /v1/triggers/{trigger_id} # Get All Trigger Events Source: https://docs.noxus.ai/api-reference/v1--triggers/get-all-trigger-events get /v1/triggers/events Browse every trigger event in the workspace, across all triggers. # Get Assistant Triggers Source: https://docs.noxus.ai/api-reference/v1--triggers/get-assistant-triggers get /v1/agents/{assistant_id}/triggers # Get Workflow Trigger Events Source: https://docs.noxus.ai/api-reference/v1--triggers/get-workflow-trigger-events get /v1/workflows/{workflow_id}/triggers/{trigger_id}/events Browse the events a specific trigger has received. # Get Workflow Triggers Source: https://docs.noxus.ai/api-reference/v1--triggers/get-workflow-triggers get /v1/workflows/{workflow_id}/triggers List a workflow's triggers. # Update Workflow Trigger Source: https://docs.noxus.ai/api-reference/v1--triggers/update-workflow-trigger patch /v1/workflows/{workflow_id}/triggers/{trigger_id} Update a trigger's definition. # Get Image Source: https://docs.noxus.ai/api-reference/v1--upload/get-image get /v1/file/{name} # Get Image Metadata Source: https://docs.noxus.ai/api-reference/v1--upload/get-image-metadata get /v1/file/{name}/metadata # Public Upload File Source: https://docs.noxus.ai/api-reference/v1--upload/public-upload-file post /v1/file # Create Variable Source: https://docs.noxus.ai/api-reference/v1--variables/create-variable post /v1/variables Create a variable or secret. # Delete Variable Source: https://docs.noxus.ai/api-reference/v1--variables/delete-variable delete /v1/variables/{variable_id} Delete a variable or secret. # List Variables Source: https://docs.noxus.ai/api-reference/v1--variables/list-variables get /v1/variables List this workspace's variables (secret values are never returned). # Update Variable Source: https://docs.noxus.ai/api-reference/v1--variables/update-variable patch /v1/variables/{variable_id} Update a variable or secret. # Create workflow public Source: https://docs.noxus.ai/api-reference/v1--workflows/create-workflow-public post /v1/workflows # Delete workflow public Source: https://docs.noxus.ai/api-reference/v1--workflows/delete-workflow-public delete /v1/workflows/{workflow_id} # Get Workflow Logs Columns Source: https://docs.noxus.ai/api-reference/v1--workflows/get-workflow-logs-columns get /v1/workflows/{workflow_id}/logs/columns # Get Workflow Logs Public Source: https://docs.noxus.ai/api-reference/v1--workflows/get-workflow-logs-public get /v1/workflows/{workflow_id}/logs # Get Workflow Public Source: https://docs.noxus.ai/api-reference/v1--workflows/get-workflow-public get /v1/workflows/{workflow_id} # Get Workflows Public Source: https://docs.noxus.ai/api-reference/v1--workflows/get-workflows-public get /v1/workflows # Public Create Workflow Version Source: https://docs.noxus.ai/api-reference/v1--workflows/public-create-workflow-version post /v1/workflows/{workflow_id}/versions # Public Export Workflow Source: https://docs.noxus.ai/api-reference/v1--workflows/public-export-workflow post /v1/workflows/{workflow_id}/export Export a workflow and all its dependencies. Returns an export file (v4 plaintext YAML by default, v3 base64 on request) that can be imported later. # Public Export Workflow Preview Source: https://docs.noxus.ai/api-reference/v1--workflows/public-export-workflow-preview get /v1/workflows/{workflow_id}/export/preview Preview what will be exported for a workflow. Returns a list of entities that would be exported along with any warnings (e.g., KBs that are too large). # Public Get Workflow Versions Source: https://docs.noxus.ai/api-reference/v1--workflows/public-get-workflow-versions get /v1/workflows/{workflow_id}/versions # Public Import Workflow Source: https://docs.noxus.ai/api-reference/v1--workflows/public-import-workflow post /v1/workflows/import Import a workflow from an export file. The definition should be the base64-encoded content from a previous export. Returns a list of all created entities with their new IDs. # Public Update Workflow Version Source: https://docs.noxus.ai/api-reference/v1--workflows/public-update-workflow-version patch /v1/workflows/{workflow_id}/versions/{version_id} # Update workflow public Source: https://docs.noxus.ai/api-reference/v1--workflows/update-workflow-public patch /v1/workflows/{workflow_id} # Agents Source: https://docs.noxus.ai/core/concepts/agents AI assistants that help automate tasks through natural conversation ## What is an Agent? An agent in Noxus is a visual AI assistant built by connecting capabilities into a conversational interface. Agents enable: * **Natural Language Processing**: Process requests naturally, maintain context, and adapt to user needs * **Tool Integration**: Access web research, human-in-the-loop, knowledge bases, flow execution, and file handling capabilities. * **Context Awareness**: Remember conversation history and maintain context across interactions * **Smart Decision Making**: Make intelligent choices based on context and available tools ## Configuration Options Agents provide extensive configuration options to customize their behavior, performance, and capabilities according to your specific needs. **AI Engine Control**: Configure how your agent thinks and responds **Resource Management**: Manage usage limits and conversation flow **Capability Definition**: Define available tools and integrations ### Model Settings Configure how your agent thinks and responds using our AI model settings to achieve the perfect balance of creativity, accuracy, and performance. **Core Configuration Options:** * **Model Selection**: Choose from various providers like OpenAI, Anthropic, and others. * **Temperature**: Control response creativity (0.0 for focused, 1.0 for creative) * **Max Tokens**: Set response length limits * **Response Style**: Configure tone and formatting preferences * **Instructions**: Define system guidance and allowed behaviors to steer responses and restrict access Each provider offers unique capabilities and specializations. Learn more about available models in our [Models & Providers guide](/core/concepts/models). ### Conversation Control Manage how your agent handles conversations and resources to optimize performance while maintaining cost-effectiveness. **Resource and Flow Control:** * **Token Management**: Define usage budgets and per-conversation limits * **Rate Limiting**: Set maximum request frequency to control throughput * **Agent Timeout**: Define maximum execution time per request to prevent long-running operations * **Generation Control**: Set safeguards like max output length, stop sequences, and sensitivity levels * **Error Recovery**: Configure retry and fallback behaviors ### Tool Access Agents can access various tools to enhance their capabilities, with each tool configurable for specific roles and requirements. **Enhanced Functionality:** * **Web Research**: Search and analyze online information * **Knowledge Bases**: Query your organization's knowledge repositories * **Flow Execution**: Run automated processes and workflows * **File Operations**: Handle document processing and management * **API Integration**: Connect with external services * **Human Handoff**: Escalate to human operators when needed Tools can be enabled or disabled based on your agent's specific role and requirements. Each tool can be configured with its own permissions and usage limits. ## Reasoning Capabilities | Level | Description | | :--------- | :----------------------------------------------- | | **Off** | Direct responses without additional analysis | | **Low** | Basic reasoning for simple decision-making | | **Medium** | Detailed analysis considering multiple factors | | **High** | Comprehensive reasoning with thorough evaluation | Reasoning capabilities are available only with select AI models that support advanced reasoning features. Agents using these models can adjust their reasoning depth based on task complexity, allowing you to balance performance with computational requirements for optimal results. ## Running Agents Each agent operates in its own secure environment, maintaining conversation context and managing its own resources for optimal performance and security. Choose how to deploy and interact with your agents based on your specific integration needs: The platform interface provides the most intuitive way to interact with your agents: * Real-time conversations with monitoring capabilities * Dynamic settings and configuration adjustments with live testing * Comprehensive analytics and conversation history * Direct management of knowledge base connections * Tool permissions and capability control Ideal for testing, training, and day-to-day agent management with full visibility into performance metrics. Our [REST API](/api-reference/introduction) enables complete integration into your existing systems: * Programmatic conversation lifecycle management * Fine-grained configuration control * Real-time performance metrics access * Synchronous and asynchronous operations * Custom integration scenarios support Perfect for building customer service platforms, internal automation systems, and custom applications with full programmatic control. The Noxus [SDK](/sdk/api-reference/introduction) empowers sophisticated application development: * Custom chat interfaces with brand matching * Type-safe access to all agent capabilities * Built-in lifecycle management and event processing * Custom tool integrations and specialized interfaces * Accelerated development with best practices Enables developers to focus on application logic while leveraging the full power of agents with enterprise-grade reliability. ## Next Steps * Learn about [Knowledge Bases](/core/concepts/knowledge-bases) * Explore [Flows](/core/concepts/flows) # Flows Source: https://docs.noxus.ai/core/concepts/flows Understanding Flows and how they empower your automation ## What is a Flow? A flow is a visual automation built by connecting nodes into a directed graph. Each node performs a specific operation, and connections define how data moves through the process. **Key Benefits:** * Visual design for complex automation logic * Clear data flow and dependencies * Built-in error handling and recovery * Real-time execution monitoring * Comprehensive analytics and observability Flows are blueprints for automation—showing how operations connect and work together to accomplish tasks. ## Key Concepts ### Building Blocks Nodes are the fundamental components of flows. Each performs a specific function—AI processing, data transformation, logic control, or external integrations. Handle text, files, images, and structured data AI Processing, Data & Files, and Logic & Control Connect external services and APIs Automate with events, schedules, and webhooks ### Data Flow Connections between nodes define data movement. Typed connections ensure compatibility and prevent errors. Branching enables conditional logic and dynamic behavior. ### Subflows Reusable flows that function as single nodes within other flows. Encapsulate logic, define inputs/outputs, and maintain consistency across projects. ### Running a Flow The platform interface provides an intuitive way to execute and test your flows. You can: * Instantly run flows with custom input parameters * Monitor execution progress in real-time * Build and access detailed logs for debugging * Get immediate visual feedback during development This method is particularly useful for iterative development and on-demand processing where immediate feedback is essential. [Triggers](/platform/flows/triggers) transform your flows into automated processes: * **Time-based schedules** for recurring tasks * **Webhooks & Integrations** to respond to external events Triggers enable sophisticated automation where one flow's completion can initiate another, creating powerful workflow orchestration. [The REST API](/api-reference/introduction) enables seamless integration with your applications and systems: * **Programmatic execution** with custom parameters * **Status tracking** and result retrieval * **Sync/async calls** for different use cases Ideal for building custom applications or integrating flows into existing systems with full programmatic control. [Agents](/core/concepts/agents) execute flows conversationally: * Natural language flow execution * Context-aware processing * AI-driven automation orchestration ## Analytics & Observability All flows include comprehensive monitoring and governance: * **Real-time Analytics** - Execution metrics, performance data, and resource usage * **Detailed Logging** - Complete execution traces and debugging information * **Evaluations** - Manual and automated quality assessments * **Safety Guardrails** - Organization-level controls and compliance policies * **Governance** - Audit trails, access controls, and regulatory compliance ## Next Steps * Learn about [node types](/platform/flows/introduction) * Explore [triggers](/platform/flows/triggers) * Understand [AI models](/core/concepts/models) # Knowledge Bases Source: https://docs.noxus.ai/core/concepts/knowledge-bases Building and managing knowledge repositories for AI interactions ## What is a Knowledge Base? Knowledge Bases in Noxus are intelligent data repositories that enhance AI capabilities with domain-specific information. They process and store information in a way that makes it readily accessible for AI operations, maintaining context and relationships between different pieces of information. ## Content Types | Type | Supported Formats | Description | Use Cases | | :------------ | :---------------------------------------- | :--------------------------------------------------- | :----------------------------------------------------- | | **Documents** | PDF, DOCX, PPTX, TXT, RTF, HTML, Markdown | Text-based content that can be processed and indexed | Documentation, guides, policies, presentations | | **Data** | CSV, JSON, XML | Tabular and structured data formats | Data analysis, configuration files, structured content | | **Images** | JPEG, PNG | Visual content that can be analyzed and indexed | Diagrams, charts, visual documentation, screenshots | | **Archives** | ZIP | Compressed files containing multiple documents | Bulk document uploads, archived content | | **Email** | EML (RFC822) | Email messages with metadata and attachments | Email archives, communication records | **Metadata Support**: All file types support custom metadata fields that can store additional information such as author, department, creation date, tags, or any custom attributes relevant to your organization. This metadata enhances search capabilities and provides richer context for AI operations. ## Core Capabilities Knowledge Bases offer extensive configuration options and capabilities to optimize performance for your specific use case: | Configurationdd | Options | Description | | :--------------------- | :----------------------------------------------------------- | :---------------------------------------------- | | **Search Methods** | Semantic, Keyword, Hybrid, Hybrid with Reranking, RRF Hybrid | Configure how content is searched and retrieved | | **Embedding Models** | Text Embeddings, Multimodal Embeddings | Choose AI models for content understanding | | **Data Sources** | Documents, Google Drive, OneDrive, Website | Control allowed content sources | | **Ingestion Settings** | Processing methods, chunking strategies | Customize how content is processed and stored | ### Search Methods **Semantic Search** - Uses vector embeddings to understand the semantic meaning of content, enabling natural language understanding beyond keywords, context-aware search results, and conceptual relationship matching. Best for complex queries and concept discovery. **Keyword Search** - Uses BM25 ranking for fast, precise text matching with exact term matching and relevance scoring. Provides fast performance for large datasets and precise results for specific terminology. **Hybrid Search** - Combines semantic understanding with keyword precision using score fusion techniques. Offers balanced results for diverse query types with configurable fusion methods (RRF, relative scoring) and optional AI-powered reranking. ## Accessing Knowledge Bases Knowledge Bases offer multiple interfaces for access and management, each suited to different use cases: Our web-based platform provides an intuitive interface for comprehensive knowledge management: * Upload and organize documents with drag-and-drop simplicity * Monitor system usage and track performance metrics * Configure different search methodologies * Real-time search and content preview capabilities Perfect for content managers and teams who need comprehensive knowledge management with full visibility into system performance. The [REST API](/api-reference/introduction) provides programmatic access to all knowledge base capabilities: * Query your knowledge repositories with advanced search parameters * Update content dynamically and manage permissions programmatically * Comprehensive usage monitoring and version control features * Batch operations for efficient bulk content management * Real-time synchronization with external data sources Ideal for building custom applications and integrating knowledge bases into existing enterprise systems with full programmatic control. The Noxus [SDK](/sdk/api-reference/introduction) enables developers to build sophisticated knowledge management solutions: * Create custom search interfaces that match your user experience requirements * Implement specialized content processors for unique data types * Build automated content management workflows * Real-time update capabilities with event-driven architecture * Advanced security and reliability features Enables developers to focus on application logic while leveraging enterprise-grade knowledge management capabilities. Knowledge Bases seamlessly integrate with [Agents](/core/concepts/agents) to provide intelligent, context-aware conversations: * **Automatic Context Retrieval**: Agents automatically search relevant knowledge when users ask questions * **Citation and Source Tracking**: Responses include references to specific documents and sections * **Real-time Knowledge Access**: Agents can access the most up-to-date information from your knowledge base * **Multi-modal Understanding**: Process both text queries and document uploads in conversations * **Conversational Memory**: Maintain context across conversations while accessing knowledge resources Agents with knowledge base integration create powerful AI assistants that can answer complex questions using your organization's specific information. Knowledge Bases can be integrated into [Flows](/core/concepts/flows) to create automated knowledge-driven processes: * **Dynamic Knowledge Retrieval**: Query knowledge bases as part of automated workflows * **Content Processing Pipelines**: Automatically process and index new documents * **Knowledge-Driven Decision Making**: Use knowledge base results to control flow logic * **Multi-source Information Aggregation**: Combine data from multiple knowledge bases * **Automated Content Updates**: Keep knowledge bases current with scheduled workflows Flows with knowledge base integration enable sophisticated automated processes that can leverage your organization's knowledge for decision-making and content generation. ## Next Steps * Learn about [Agents](/core/concepts/agents) * Explore [Flows](/core/concepts/flows) * Review [best practices](/platform/overview) # Users Source: https://docs.noxus.ai/core/concepts/users Manage team members, permissions, and access control Users in Noxus represent individual team members who have access to your workspaces. Each user has their own credentials, role, and activity tracking across one or more workspaces. ## User Profile Each user has a personal profile that applies across all workspaces. It carries the following information: * Name and display name * Email address (login credential) * Profile picture/avatar ### User Roles Noxus provides a role-based access control (RBAC) system with clearly defined permissions. There are two role types: * **Organization role** - Defines the permissions at the organization (platform) level. * **Workspace role** - Defines the permissions at the workspace level. One per workspace. When a user is an admin in the organization, they will also be an admin on all workspaces. To learn more, see [Permissions & roles](/platform/concepts/permissions). ### API Keys API keys provide programmatic access to a specific workspace. Each key is scoped to one workspace and can optionally be restricted to a subset of that workspace's permissions. ```bash theme={null} # Using an API key curl -X GET https://api.noxus.ai/v1/workflows \ -H "Authorization: Bearer noxus_abc123..." ``` Keys are created from **Workspace Control** → **API keys**. When creating a key you can toggle **Restrict permissions** to limit the key to only specific workspace permissions. A key can never have more permissions than the user who created it. If a user's permissions are reduced, any keys they created retain only the permissions that still overlap. *** ## User Management ### Adding New Users Add new members to your organization: Go to Settings → Users Click "Add user" and: * Enter their name, email address, and organization role * Select which workspaces to add the user to Click "Next" and select the role for each workspace (platform admins are admins on all workspaces) Click "Add". The invited user will receive an email with the invitation ### Adding Existing Users to a Workspace Invite team members to a workspace: Go to Workspace control → Users Click "Add users" and select which users to add Select the appropriate role for each user Click "Add". The user will be added to that workspace You can also use the API and SDK to add users to the platform. ### User Onboarding and Offboarding When a user first joins a workspace: 1. **Email invitation** - Receives an invite with workspace details. 2. **Account creation** - Creates an account or signs in to an existing one. 3. **Workspace tour** - Optional guided tour of key features. 4. **Role assignment** - Receives an assigned role and permissions. 5. **Resource access** - Can immediately access resources based on their role. ### Training & Resources Comprehensive platform documentation. Step-by-step video guides (coming soon). Templates and examples to learn from inside the platform. Reach out to our support team. When a removing a user: 1. **Review access** - Audit what resources the user created or has access to. 2. **Revoke access** - Remove the user from the organization. 3. **Invalidate keys** - Rotate any API keys or credentials the user had access to. ### Preserving Work User-created resources remain in the workspace: * Workflows continue to function * Agents remain operational * Knowledge bases stay intact * Activity logs are preserved Removing a user immediately revokes all access. Ensure critical resources are reassigned first. *** ## Best Practices * **Follow Least Privilege**: Grant the minimum permissions required for each user's role. * **Regular Access Reviews**: Quarterly review of user permissions and adjust as needed. * **Separate Roles by Function**: Don't make everyone an Admin; use appropriate roles. * **Use Service Accounts**: For integrations, avoid using personal accounts. * **Document Roles**: Maintain documentation of who has what access and why. * **Clear Ownership**: Assign clear ownership for important resources. * **Offboarding Checklist**: Follow a consistent process when users leave. * **Training for New Users**: Ensure new team members understand their permissions and responsibilities. *** ## Other Core Concepts Isolated environments for your projects and resources Start building flows in your workspace Create and develop AI agents for your team Intelligent data repositories to enhance your tools # Workspaces Source: https://docs.noxus.ai/core/concepts/workspaces Organize teams, projects, and resources in isolated environments ## What is a Workspace? A Workspace in Noxus is an isolated environment that contains a set of your AI resources, workflows, agents, knowledge bases, and team members. Workspaces provide organizational boundaries, resource isolation, and access control for teams and projects. Think of a workspace as a container for everything related to a specific team, department, or project. Each workspace operates independently with its own resources, users, and settings. ### Key Concepts **Resource Separation** - Each workspace maintains its own flows, agents, knowledge bases, and data, completely isolated from other workspaces. **Independent Configuration** - Workspaces have separate settings, integrations, model configurations, and API keys. **Access Boundaries** - Users and permissions are scoped to individual workspaces, ensuring proper access control. **Billing Isolation** - Usage and costs are tracked separately per workspace for clear accounting and charge-back. ### Use Cases Isolate different teams (Marketing, Sales, Engineering) with their own resources and access Maintain separate workspaces for Development, Staging, and Production Create dedicated workspaces for different clients or customer projects Separate workspaces for different compliance requirements or data classifications *** ## Workspace Resources Each workspace is an isolated container for the things you build and the access controls around them. ### AI tools (flows, agents, knowledge bases) Everything you create is scoped to the workspace, including: * Definitions and versions. * Runs, history, and analytics. * Scheduled triggers and deployments. ### Integrations Connections to external services, including: * OAuth connections (Google, Microsoft, etc.). * API credentials and authentication settings. * Integration-specific configuration. * Usage tracking per integration. ### Users & Permissions Workspace members and what they’re allowed to do, including: * Invitations and member management. * Role-based access control. * Permission assignments. * Activity and audit logs. *** ### Integrations Manage connected external services: * Google Workspace * Microsoft 365 * Slack * GitHub * And more... * Custom API integrations * Database connections * Third-party services * Webhook endpoints * Secure credential storage * Rotation policies * Access audit logs * Sharing controls *** ## Managing Workspaces A user can be on multiple Workspaces at the same time. They can use the Workspace Selector on the top of the sidenav to switch between them. On click, all views and resources update to the selected workspace. Users can only switch to Workspace they have access to. Platform admins **can access** all workspaces in the platform, wven if not directly set as users on them. ### Creating a Workspace - Option 1 Click on the workspace name in the top of the sidenav Select "Create New Workspace" from the dropdown Set name, description, and color ### Creating a Workspace - Option 2 Click on your name in the bottom of the sidenav and select Settings Select "Workspaces" option from the sidenav Click on the button on the top right of the page Set name, description, and color Some billing plans limit how many workspaces a user can create. Enterprise plans include unlimited workspaces. ### Editing a Workspace * From 'Workspace Control' on the bottom of the sidenav
* by clicking on a Workspace from the list of Workspaces in the Settings
Add users and assign roles. Set up API keys, integrations, and defaults
Some actions depend on the role of a user. ### Workspace Roles Different roles have varying levels of access within a workspace: | Role | Description | Permissions | | :--------- | :---------------------------- | :---------------------------------------------------- | | **Admin** | Full workspace control | All permissions including `workspace_admin` | | **Editor** | Build and deploy AI resources | Create/edit/delete flows, agents, and knowledge bases | | **Reader** | Run and query resources | `flows_run`, `agents_run`, `kbs_query` | You can create custom workspace roles with any combination of permissions. To learn more, see [Permissions & roles](/platform/concepts/permissions). *** ## Multi-Workspace Patterns Noxus provides the freedom for users to use Workspaces in multiple ways. Some recommended patterns include: Create separate workspaces for each environment: **Development workspace** * Experimentation and testing * Rapid iteration without risk * Test data and mock integrations * Team sandbox environment **Staging workspace** * Pre-production testing * Integration testing with real-ish data * User acceptance testing * Performance validation **Production workspace** * Live, customer-facing deployments * Production data and integrations * Monitored and secured * Change control processes This pattern ensures changes are thoroughly tested before reaching production, reducing risk and improving reliability. Organize by team or department: **Marketing workspace** * Content generation workflows * Social media automation * Campaign analytics * Marketing team access only **Sales workspace** * Lead qualification agents * CRM integrations * Sales enablement workflows * Sales team access only **Customer support workspace** * Support chatbots * Ticket routing workflows * Knowledge base for support articles * Support team access only For agencies or consultants managing multiple clients: **Client A workspace** * Client-specific workflows and agents * Client A's integrations and data * Isolated billing and usage tracking * Controlled access for client team **Client B workspace** * Separate resources for Client B * Independent configurations * Isolated data and processing * Client B team access Always use separate workspaces for different clients to ensure data isolation, security, and compliance with client agreements. *** ## Best Practices * **Use descriptive names**: Name workspaces clearly to indicate their purpose (e.g., "Acme Corp - Production", "Marketing - Dev"). * **Separate environments**: Always maintain separate workspaces for development and production. * **Limit production access**: Restrict production workspace access to essential team members only. * **Document workspace purpose**: Use the description field to explain the workspace's purpose and any special configurations. * **Enable MFA**: Require multi-factor authentication for all workspace members. * **Review access regularly**: Audit user access quarterly and remove unnecessary permissions. * **Use service accounts**: Create service accounts for API access rather than using personal credentials. * **Rotate credentials**: Regularly rotate API keys and integration credentials. * **Consolidate when possible**: Don't create unnecessary workspaces; use permissions within a workspace when appropriate. * **Monitor usage**: Regularly review workspace usage and costs. * **Clean up unused resources**: Archive or delete workflows and agents that are no longer needed. * **Set naming conventions**: Establish consistent naming patterns for resources across workspaces. *** ## Workspace Migration ### Exporting Resources Export flows, agents, and configurations from one workspace: * Export individual flows as JSON * Export knowledge base configurations * Export agent definitions * Document integration settings Knowledge Bases data will need to be reingested when they are imported into a Workspace. If the amount of files is too big, it might be impossible to export a Knowledge Base. ### Importing Resources Import exported resources into another workspace: * Import flow definitions * Recreate agent configurations * Import knowledge base settings * Reconfigure integrations with new credentials Integrations and credentials must be reconfigured in the target workspace as they are not exportable for security reasons. *** ## Next Steps Learn about user management and permissions Start building flows in your workspace Create and develop AI agents for your team Intelligent data repositories to enhance your tools # Getting Started Source: https://docs.noxus.ai/core/getting-started Welcome to Noxus — Your new AI Platform Noxus helps teams ship AI automations fast by combining **Flows**, **Agents**, and **Knowledge Bases** in one platform, backed by production-ready infrastructure. ## Start here If you’re new to Noxus, start here to learn the essentials and build your first tools. Understand the Noxus ecosystem and what you can build on it. Watch quick walkthroughs and end-to-end examples. Go to the platform and start building. ## Choose how you want to use Noxus Our visual in browser platform, where you can build all your AI tools. Integrate Noxus into Python apps and automate AI programmatically. Use Noxus from any language via HTTP endpoints. Extend the platform with custom nodes, and integrate with your tools. *** ## Documentation map Use this section as a shortcut to the right part of the docs. ### Platform overview A product-level explanation of the Noxus ecosystem. Workspaces, users, flows, agents, and knowledge bases. ### Build on the platform Start building flows in your workspace Create and develop AI agents for your team Intelligent data repositories to enhance your tools Connect Noxus to your existing tools. ### Programmatic access Client setup, auth, and core primitives. Authentication and endpoint conventions. ### Infrastructure & deployment Noxus runtime architecture. Deployment options like VM, Kubernetes, and Cloud Run. ### Development & plugins What can you build with Noxus plugins Step-by-step tutorials and examples. *** ## Quick navigation by role ### I’m a builder (platform user) * Build your first flow in the [Visual editor](/core/platform/editor) * Add retrieval with [Knowledge bases](/platform/knowledge-bases/introduction) ### I’m integrating via code (developer) * Use the [SDK](/sdk/introduction) or [API](/api-reference/introduction) * Learn the primitives: [Flows](/platform/flows/introduction) and [Agents](/platform/agents/introduction) ### I’m deploying infra (infra/security) * Start with [Infrastructure overview](/core/infrastructure-overview) * Review [Security](/core/infrastructure/security) * Configure [Models & providers](/core/infrastructure/models-providers) ### I’m extending Noxus (plugins) * Start with your [First plugin](/developers/plugins/your-first-plugin) * If you’re building nodes, start at [Platform → Extending](/core/platform/extending) *** ## Need Help? Get help from our team with setup, deployment, or usage questions See Noxus in action with a personalized demo # Overview Source: https://docs.noxus.ai/core/infrastructure-overview Understanding Noxus deployment and infrastructure options Noxus is designed to meet the needs of organizations of all sizes, from startups to enterprises, with flexible deployment options that balance ease of use with control over infrastructure and security. ## Deployment Options Fully managed platform with zero infrastructure overhead Deploy on your own infrastructure with complete control Hybrid and air-gapped architectures for complex requirements *** ## Choosing Your Deployment Model Evaluate data residency, compliance, security, and control needs Determine available infrastructure and operational expertise Estimate workload volume and growth trajectory Select the model that best aligns with your requirements and resources Design your worker pools, security zones, and resource allocation ## Next Steps Start building immediately with our managed cloud platform Explore detailed deployment guides Understand the platform architecture and components Implement security for your deployment *** For personalized guidance on choosing the right deployment model for your organization, contact our team at [help@noxus.ai](mailto:help@noxus.ai). # Deployments Source: https://docs.noxus.ai/core/infrastructure/deployments Deployment options and configurations Noxus is designed to meet the needs of organizations of all sizes, from startups to enterprises, with flexible deployment options that balance ease of use with control over infrastructure and security. ## SaaS Deployment The fastest way to get started with Noxus. Our managed cloud platform handles all infrastructure, scaling, updates, and maintenance. ### Key Features We handle servers, scaling, backups, updates, and monitoring so you can focus on building AI solutions. SOC 2 Type II certified infrastructure with encryption at rest and in transit, regular audits, and compliance. Multi-region deployment with automatic failover ensures your AI workflows run reliably anywhere in the world. Resources scale automatically based on demand, from a few workflows to thousands of concurrent executions. ### Ideal For * Teams who want to get started quickly without infrastructure expertise * Organizations prioritizing speed to market over infrastructure control * Projects with variable or unpredictable workloads * Teams without dedicated DevOps or infrastructure resources Create a free account and start building in minutes *** ## On-Premises Deployment Deploy Noxus on your own infrastructure for maximum control over data, security, and compliance. ### Key Features * **Complete Data Control**: All data remains within your infrastructure, never leaving your network perimeter. * **Customizable Architecture**: Deploy on VMs, Kubernetes, or container platforms tailored to your practices. * **Network Isolation**: Run in isolated networks, private clouds, or air-gapped environments. * **Security Compliance**: Meet strict regulatory requirements (HIPAA, GDPR, FedRAMP, etc.) with full control. * **Separate Worker Pools**: Isolate workloads by creating dedicated pools for different teams or security zones. * **Resource Optimization**: Allocate specific CPU, memory, and GPU resources for optimal performance. ### Deployment Platforms **Traditional VM Deployment** Deploy on physical or virtual machines with systemd services: * Simple architecture for teams familiar with VM management * Predictable resource allocation * Direct control over system resources Complete setup instructions for VM-based deployments **Container Orchestration** Deploy using Kubernetes for scalability and resilience: * Automatic scaling based on load * Self-healing and rolling updates * Multi-node high availability Helm charts and configuration for Kubernetes deployments **Serverless Containers** Deploy on managed container platforms: * Simplified scaling and management * Pay-per-use pricing model * Reduced operational overhead Deploy on Google Cloud Run or similar platforms ### Ideal For * Enterprises with strict data residency or compliance requirements * Organizations that need complete control over infrastructure and security * Regulated industries (healthcare, finance, government) * Teams with existing infrastructure investments View detailed deployment documentation and architecture guides *** ## Specialized Deployments For organizations with complex security or architectural needs, Noxus supports hybrid and fully air-gapped configurations. ### Hybrid Deployment Patterns Combine the flexibility of cloud with the control of on-premises infrastructure: * **Control Plane in Cloud, Workers On-Premises**: Management interface in SaaS while sensitive workloads execute on your infrastructure via encrypted tunnels. * **Multi-Region Deployment**: Deploy across multiple data centers or cloud regions for geo-distributed execution and regional compliance. * **Tiered Processing**: Less sensitive workflows in the cloud for scalability, with regulated processing kept on-premises. ### Air-Gapped Capabilities For the most security-sensitive environments that prohibit internet connectivity: * **Fully Offline Operation**: Complete platform functionality without any external network access. * **Update Management**: Receive updates via secure offline transfer mechanisms with cryptographic verification. * **Internal Model Registry**: Host AI models and embeddings within your isolated network. * **Isolated Knowledge Bases**: All data processing and retrieval happens within your secured perimeter. ### Ideal For * Government and defense organizations * Critical infrastructure operators * Financial institutions with strict security policies * Research facilities with classified data * Global companies with regional data compliance needs Contact our team for guidance on hybrid or air-gapped deployments # Model Presets Source: https://docs.noxus.ai/core/infrastructure/model-presets Named model configurations that flows and agents resolve at runtime ## What are Model Presets? Model presets are named bundles of model selection rules. Instead of wiring a specific model ID into every flow or agent, you reference a preset by its handle — like `cost` or `performance` — and the platform picks the best available model at runtime based on your active providers. Presets decouple your AI logic from specific model versions. When a provider releases a better model or you switch vendors, you update the preset once and every flow using it automatically benefits. A preset defines: * **A ranked list of models** — tried in order, falling back to the next if unavailable. * **Required capabilities** — e.g. `vision`, `function_calling`, `reasoning`. Only models satisfying all requirements are considered. * **Exhaustive mode** — try every model in the list before failing, rather than stopping at the first success. * **Data center fallback** — whether to allow cross-region fallback when no model is available locally. ## Preset Types Noxus ships with a set of built-in presets and lets you create your own. **Platform defaults** — the two core presets every workspace gets: `cost` and `performance`. Always available, cannot be deleted. Editing one creates a workspace-scoped override while the original is preserved. **System presets** — shipped with the platform for specific use cases like `reasoning`, `ocr`, and `chat-quality`. Visible in the UI and usable in flows, but not editable. You can override them by editing, which creates a Custom copy scoped to your workspace. **Workspace presets** — created or overridden by your organization. Fully editable, renamable, and deletable. Overrides of Default or Internal presets can be reset to restore the original system behavior. ## Managing Presets Navigate to **Organization Settings** > **Model Presets** to view and manage your workspace's presets. ### Editing a Preset Click **Edit** on any preset row. For Default and Internal presets, this creates a Custom workspace override — the original is never modified. You can configure: * **Title, subtitle, and description** — labels shown across the product. * **Icon** — visual identifier in model selection menus. * **Model list** — the ordered set of models tried at runtime. Only model IDs registered with active providers are accepted. * **Required capabilities** — restrict selection to models that support specific features. * **Sort order** — controls position in model selection dropdowns. * **Active / Inactive** — inactive presets are hidden from users but remain usable by existing flows. ### Creating a Custom Preset Click **New Preset** and fill in the form. The **handle** is a URL-safe identifier used in API calls and flow configs (e.g. `my-coding-preset`) — it cannot be changed after creation. The model list must contain at least one valid model ID. Unknown model IDs are rejected at save time. Disabled models are silently dropped — if all models in a preset are disabled, the preset will return no results at runtime. ### Resetting to Default For workspace overrides of system presets, **Reset to Default** deletes the override and restores the original. For purely custom presets with no system counterpart, the preset is deleted entirely. ## Built-in Handles Flows and agents reference presets by handle. The platform ships with the following: | Handle | Purpose | | :------------------------ | :----------------------------------------------------- | | `cost` | Lowest-cost models available | | `performance` | Highest-quality models available | | `reasoning` | Models with extended reasoning capabilities | | `ocr` | Vision-capable models for document extraction | | `chat-quality` | High-quality conversational models | | `chat-cost` | Cost-efficient conversational models | | `chat-balanced` | Balanced cost/quality for chat | | `rag-performance` | High-quality models for retrieval-augmented generation | | `rag-cost` | Cost-efficient RAG models | | `multi-modal-cost` | Cost-efficient vision + text models | | `multi-modal-performance` | High-quality vision + text models | Configure the providers that back your preset model lists. Understand how model credentials and access are protected. # Models & Providers Source: https://docs.noxus.ai/core/infrastructure/models-providers Configure and manage AI model providers, LLMs, embeddings, and health monitoring Noxus provides a unified interface to access leading AI models from multiple providers. Connect your provider credentials once and manage all your models, embeddings, and presets from a single settings page. ## Model Configuration All model and provider management lives under **Settings** > **Models**. The page is organized into four tabs: Configure and manage your AI provider connections. Each provider shows its name, how many LLMs and embeddings are linked, health status, and an active toggle. Providers tab showing connected providers with health status View all LLM models linked to your providers. Each row shows one model–provider combination with speed and quality gauges, health status, and an active toggle. Filter by provider or status. LLMs tab showing models with speed and quality gauges Same layout as LLMs but for embedding models used by knowledge bases. Manage which embedding models are active across your providers. Embeddings tab showing embedding models Named model bundles that flows and agents reference by handle. See [Model Presets](/core/infrastructure/model-presets) for details. Presets tab showing model preset configurations *** ## Supported Providers Noxus supports a wide range of cloud AI providers. Click **Add provider** to see the full list and connect a new one. Add provider dialog showing all available providers | Provider | Auth Method | Multi-Region | Models | | :------------------- | :------------------------------------------------ | :--------------- | :-------------------------------------- | | **OpenAI** | API key | — | GPT-4o, GPT-4.1, o-series, embeddings | | **Anthropic** | API key | — | Claude 4.x family | | **Google Vertex AI** | Service account, Project ID, or API key | ✅ Multi-location | Gemini family, embeddings | | **Gemini** | API key | — | Gemini family (via Google AI Studio) | | **AWS Bedrock** | AWS credentials or API key | ✅ Multi-region | Claude, Titan, Nova, embeddings | | **Azure OpenAI** | API key | — | GPT-4o, embeddings | | **Azure AI Foundry** | API key, managed identity, or default credentials | — | Claude models via Azure | | **DeepSeek** | API key | — | DeepSeek models | | **Grok (xAI)** | API key | — | Grok models | | **Groq** | API key | — | Fast inference models | | **Mistral AI** | API key | — | Mistral family | | **OpenRouter** | API key | — | 100+ open-source and proprietary models | | **Perplexity** | API key | — | Real-time search models | Noxus continuously adds support for new providers. If you don't see a specific provider listed, it may still be supported via OpenRouter or our plugin system. *** ## Connecting a Provider Navigate to **Settings** > **Models**. You'll land on the **Providers** tab. Click **Add provider** in the top right. Select **Model** or **Observability** from the left sidebar, then choose your provider. Fill in your API key or complete the authentication flow. For providers like Vertex AI and Bedrock, choose your auth mode and configure regions. Noxus automatically tests your credentials. For multi-region providers (Vertex, Bedrock), each region is tested individually with a step-by-step health pipeline. Switch to the **LLMs** or **Embeddings** tab and toggle on the models you want to make available. ### Provider Details Click any provider row to open its details drawer. Here you can see: * **Connection info** — name, active toggle, linked model counts * **Health status** — last check time, current status, and a **Test connection** button Provider details drawer showing health status and test connection *** ## Health Monitoring & Model Lifecycle Noxus continuously monitors your provider connections and model availability through automated health checks that form the **model lifecycle** system. ### Provider Health Statuses These statuses appear on the **Providers** tab and reflect the overall health of a provider connection: | Status | Meaning | | :------------ | :------------------------------------------------- | | **Healthy** | Provider is reachable and credentials are valid | | **Degraded** | Some models or regions are failing but others work | | **Unhealthy** | Provider is unreachable or credentials are invalid | ### Model Health Statuses Individual model–provider links (shown on the **LLMs** and **Embeddings** tabs) have their own lifecycle: | Status | Meaning | | :-------------- | :--------------------------------------------------------------------------------------------------------------------------------- | | **Healthy** | Model is available and included in routing | | **Degraded** | Model is reachable but experiencing intermittent failures — still included in routing but may fall back to other models | | **Suspended** | Temporarily excluded from routing after repeated consecutive failures — recovers automatically on the next successful health check | | **Deactivated** | Removed from routing after persistent failures — recovered by periodic probes or manual reactivation | ### Connection Testing When you test a provider connection, Noxus runs a multi-step health pipeline that validates each aspect of your configuration: * **Authentication** — are your credentials valid? * **Permissions** — do you have the right access level? (Vertex IAM, Bedrock invoke permissions) * **Regional access** — for multi-region providers, each configured region is tested individually Each step reports pass, fail, or skip, with actionable hints when something goes wrong. Results stream in real-time so you can see progress as each step completes. ### Automatic Lifecycle Management Noxus runs periodic health checks on all provider connections and model links in the background. Based on results, models transition automatically between lifecycle states: 1. A **healthy** model starts failing → after several consecutive failures it becomes **suspended** and is excluded from routing. 2. If failures persist → the model is **deactivated** and fully removed from routing. 3. Periodic recovery probes test deactivated models → if a probe succeeds, the model is restored to **healthy**. This means transient provider outages are handled automatically without manual intervention. You can also manually reactivate a deactivated model at any time from the LLMs or Embeddings tab. *** ## Model Selection in Flows and Agents When configuring an AI node in a flow or an agent, you select models through the **Model** tab in the configuration drawer. ### Preset Selection By default, nodes use a **model preset** — a named bundle of models tried in priority order. If the first model encounters an error, the next one is used automatically. Model picker showing a preset with fallback chain Click the preset dropdown to switch between presets or choose **Custom models** for manual selection. Preset dropdown showing available presets ### Custom Model Selection When you choose **Custom models**, a model browser opens showing all available models across your connected providers. Each model appears once per provider connection, so if you've connected both OpenAI and Azure OpenAI, you'll see separate entries for each. Model selection modal with search, filters, speed and quality gauges The model browser includes: * **Search** — find models by name * **Filters** — narrow by provider, speed, quality, location, and capabilities (vision, function calling, reasoning, etc.) * **Speed & Quality gauges** — visual indicators to compare models at a glance * **Model details** — hover over a model to see its full specs: speed (tokens/sec), quality score, context window, release date, and capabilities * **Fallback chain** — selected models are ordered by priority. The first model is the default; others are fallbacks *** ## Local & Custom Models For organizations with strict data residency requirements or proprietary models, Noxus offers deep integration for self-hosted infrastructure. ### Seamless Local Integration * **Private Endpoints**: Connect to on-premises inference servers (e.g., vLLM, Ollama, TGI) via secure private networking using custom base URLs on OpenAI-compatible providers. * **Unified Interface**: Local models appear alongside cloud providers, allowing for seamless switching in flows and agents. ### Custom Model Providers You can extend the platform to support any proprietary or specialized model through our plugin system. * **Custom Inference**: Build plugins for internal model servers or niche providers. * **Fine-tuned Models**: Easily integrate your organization's fine-tuned models into the standard workflow. *** ## Observability Providers In addition to model providers, you can connect observability backends to trace and monitor all AI model calls. See [Observability Providers](/core/infrastructure/observability-providers) for details. Configure named model bundles for consistent selection across flows. Choose the right model for your task based on quality, speed, and cost. Connect tracing backends for full visibility into model calls. Understand how your API keys and model data are protected. # Observability Providers Source: https://docs.noxus.ai/core/infrastructure/observability-providers Connect tracing backends to get full visibility into your AI model calls ## What are Observability Providers? Observability providers connect Noxus to external tracing backends so you can inspect every LLM call made by your flows and agents — inputs, outputs, token usage, latency, fallback routing, and human feedback scores — all in one place. Noxus instruments all AI activity using OpenTelemetry and the OpenInference semantic conventions. Any backend that speaks OTLP receives rich, structured traces without additional configuration. Each provider you connect receives a full copy of every trace. You can connect multiple backends simultaneously — useful for routing to both an internal Phoenix instance and a team Langfuse project at the same time. ## Supported Backends **Open-source LLM observability** — ideal for self-hosted deployments or teams that want full control over their trace data. * Self-hostable or available as [Arize Phoenix Cloud](https://phoenix.arize.com) * Rich UI for trace inspection, span timelines, and prompt analysis * Supports human feedback annotations directly from the Noxus chat interface * Authentication via Bearer token (optional for local instances) **Open-source LLM engineering platform** — includes tracing, prompt management, and evaluation workflows. * Self-hostable or available as [Langfuse Cloud](https://langfuse.com) * Supports feedback scoring from the Noxus chat interface * Authentication via public key + secret key pair * Endpoint should point to your Langfuse instance's OTLP ingest path (e.g. `https://cloud.langfuse.com/api/public/otel/v1/traces`) **Any OTLP-compatible backend** — connect Grafana Tempo, Honeycomb, Jaeger, Datadog, or any other collector that accepts OTLP over HTTP. * Provide the full OTLP endpoint URL (e.g. `https://api.honeycomb.io/v1/traces`) * Optional Bearer token for authentication * Project or service name used as the OTel resource attribute ## Connecting a Provider Navigate to **Organization Settings** > **Providers** and click **Add Provider**. Select the observability category. Pick **Phoenix**, **Langfuse**, or **OpenTelemetry** from the provider list. Fill in the endpoint URL, project name, and any required credentials. Noxus tests the connection by sending a probe span before saving. Once the connection test passes, the provider is marked active and tracing begins immediately for all new flow and agent executions. The endpoint must be the full OTLP path — Noxus does not append `/v1/traces` automatically. Double-check your backend's documentation for the exact ingest URL. ## What Gets Traced Every AI execution in Noxus produces a structured trace with the following spans: | Span | Description | | :------------------- | :----------------------------------------------------------------------------------------- | | **Agent** | Top-level span for the entire node execution. Captures the full prompt and final response. | | **LLM call** | One span per model invocation, including the model ID used, token counts, and latency. | | **Fallback routing** | Recorded when the platform falls back from one model to another within a preset. | | **Cache hit** | Recorded when a cached response is served, with the cache key and saved latency. | | **Tool call** | Spans for each tool or function call made during an agent turn. | All spans follow the [OpenInference](https://github.com/Arize-ai/openinference) semantic conventions, making them compatible with any OpenInference-aware UI. ## Human Feedback Scoring For **Phoenix** and **Langfuse** providers, users can submit thumbs-up / thumbs-down feedback on agent responses directly from the Noxus chat interface. Feedback is sent to the backend as a span annotation or score linked to the originating trace, letting you filter and analyze low-quality responses in your observability tool. ## Multiple Providers You can connect more than one observability provider at the same time. Noxus fans out every trace to all active backends in parallel — each backend receives an identical copy of the span data. This is useful for: * Sending traces to both a self-hosted Phoenix instance and a team Langfuse project * Running a new backend alongside an existing one during evaluation * Separating production and staging observability into different projects on the same backend Configure the AI model providers that power your flows. Platform-level monitoring, audit logs, and scaling. # Security Source: https://docs.noxus.ai/core/infrastructure/security Enterprise-grade security architecture and defense-in-depth strategies Noxus is built on a foundation of **defense-in-depth**, ensuring that security is integrated into every layer of the platform—from the physical infrastructure to the application code and user access controls. ## Data Protection We employ industry-standard encryption and isolation techniques to ensure your data remains confidential and tamper-proof. All data is protected using **AES-256 encryption** at rest and **TLS 1.3** in transit. This ensures that even in the event of physical theft or network interception, your information remains unreadable. Key management is handled through **HSM** or cloud-native **KMS** solutions, providing a root of trust that is physically separated from the application environment. Workspaces provide strict logical isolation between different teams and projects. Data from one workspace is never accessible to another, preventing cross-tenant leakage. For sensitive workloads, you can deploy **isolated worker pools**. This allows you to process PII or regulated data on dedicated hardware within specific security zones. *** ## Identity & Access Management (IAM) Noxus provides robust tools to control who can access your resources and what actions they can perform. ### Authentication We support modern authentication standards to ensure only authorized users can enter the platform: * **Multi-Factor Authentication (MFA)**: Mandatory for all administrative accounts. * **Single Sign-On (SSO)**: Seamless integration with enterprise identity providers (SAML, OIDC). * **Granular API Keys**: Scoped keys that follow the principle of least privilege. ### Role-Based Access Control (RBAC) Access is managed through a sophisticated permissions system: * **Predefined Roles**: Quick-start with roles like Admin, Developer, and Viewer. * **Custom Scopes**: Create bespoke roles tailored to your organization's specific workflow requirements. * **Audit Logging**: Every action—from login to flow execution—is recorded in a tamper-proof audit trail. *** ## Network & Infrastructure Security Whether you are on our SaaS platform or running on-premises, your network perimeter is protected by multiple layers of defense. * **DDoS Protection**: Automated mitigation against large-scale network attacks. * **Web Application Firewall (WAF)**: Filters out common web exploits like SQL injection and cross-site scripting (XSS). * **VPC Isolation**: All SaaS resources run within isolated Virtual Private Clouds. * **mTLS**: Service-to-service communication is encrypted and authenticated using mutual TLS. * **Private Networking**: Worker pools communicate with the control plane over secure, private tunnels. * **IP Allowlisting**: Restrict access to the platform or specific APIs to known corporate IP ranges. *** ## Compliance & Monitoring We maintain a proactive security posture through continuous monitoring and adherence to global standards. ### Auditability Noxus provides high-fidelity audit trails to ensure every action is accountable and traceable: * **Comprehensive Audit Logs**: Every administrative and management action—including resource creation, role updates, and flow executions—is recorded with full context (user identity, timestamp, and payload). * **API Call Logging**: Detailed tracking of every incoming request, including response codes, duration, and the specific API key or user responsible. * **Tamper-Proof Storage**: Logs are stored in a dedicated persistence layer and can be exported to external SIEM platforms for long-term retention and forensic analysis.
### Certified Standards * **SOC 2 Type II**: Verified operational security and data privacy. * **GDPR**: Full compliance with European data protection regulations. * **HIPAA**: Eligible for healthcare workloads in on-premises deployments. * **ISO 27001**: Framework implementation currently in progress.
### Proactive Monitoring * **Intrusion Detection (IDS)**: Real-time monitoring for suspicious system behavior. * **Anomaly Detection**: AI-powered alerts for unusual usage patterns or access attempts. * **SIEM Integration**: Export audit and system logs to your corporate security operations center.
*** ## Security Best Practices Require multi-factor authentication for all users across the organization. Assign users only the minimum permissions necessary for their specific role. Regularly rotate API keys and integration credentials to minimize the impact of potential leaks. Review audit logs and user access permissions on a monthly basis. Learn how to implement advanced security configurations for enterprise deployments. # REST API Source: https://docs.noxus.ai/core/platform/api HTTP API for all platform features Language-agnostic HTTP API for integrating Noxus into any application or platform. ## Key Features Access all Noxus capabilities through RESTful HTTP endpoints with standard request/response patterns. Integrate from any language or platform that can make HTTP requests (JavaScript, Java, Go, Ruby, etc.). Configure webhooks to receive real-time notifications about flow completions, agent actions, and system events. Complete OpenAPI/Swagger documentation for automatic client generation and API exploration. ## Common Use Cases Build web or mobile interfaces that interact with Noxus: ```javascript theme={null} // React example const runFlow = async (inputs) => { const response = await fetch('https://backend.noxus.ai/v1/workflows/wf_123/runs', { method: 'POST', headers: { 'X-API-KEY': apiKey, 'Content-Type': 'application/json' }, body: JSON.stringify({ input: inputs }) }); return await response.json(); }; ``` Connect Noxus to your microservices architecture: ```go theme={null} // Go example type FlowRequest struct { Input map[string]interface{} `json:"input"` } func runNoxusFlow(flowID string, inputs map[string]interface{}) error { req := FlowRequest{Input: inputs} body, _ := json.Marshal(req) resp, err := http.Post( fmt.Sprintf("https://backend.noxus.ai/v1/workflows/%s/runs", flowID), "application/json", bytes.NewBuffer(body), ) // Handle response... } ``` Trigger flows from external events: ```bash theme={null} # Stripe webhook example curl -X POST https://backend.noxus.ai/v1/workflows/wf_123/runs \ -H "X-API-KEY: ${API_KEY}" \ -H "Content-Type: application/json" \ -d '{ "input": { "event": "payment_succeeded", "customer_id": "cust_123", "amount": 4999 } }' ``` Integrate AI flows into your deployment pipeline: ```yaml theme={null} # GitHub Actions example - name: Run Noxus Flow run: | curl -X POST https://backend.noxus.ai/v1/workflows/wf_123/runs \ -H "X-API-KEY: ${{ secrets.NOXUS_API_KEY }}" \ -H "Content-Type: application/json" \ -d '{"input": {"pr_number": "${{ github.event.pull_request.number }}"}}' ``` Connect Noxus to platforms like Zapier, Make, or n8n: * Create custom Zapier actions * Build Make.com modules * Add n8n nodes * Integrate with Pipedream flows ## Authentication All API requests require authentication via API keys provided in the `X-API-KEY` header: ```bash theme={null} curl -X GET https://backend.noxus.ai/v1/workflows \ -H "X-API-KEY: your_api_key_here" ``` Store API keys securely and never commit them to version control. Use environment variables or secret management systems. ## Getting Started Create an API key from your workspace control Make a test request to verify your credentials Browse the complete API reference Build your integration using the API Complete API documentation with all endpoints and examples # Visual Editor Source: https://docs.noxus.ai/core/platform/editor No-code interface for building flows and agents The web-based editor provides an intuitive, no-code interface for building AI automation. When you first log in, you land on the **Overview**, which provides a high-level view of your current workspace and quick access to your most recent resources. Dashboard Overview The interface is designed to keep your focus on building while providing easy access to organization-wide settings and workspace-specific tools. From here, you can navigate between different workspaces using the picker, or dive into the AI Tools to start building flows and agents. ## Navigation The platform is organized around workspaces, allowing you to switch between different environments and access core tools. All resources and tools visible in the sidebar—including the AI Tools, App Library, and Templates—are strictly constrained to the currently selected workspace. Switching workspaces will update these views to reflect the resources of the new environment. Workspace Selector Switch between different isolated environments. Each workspace has its own set of members, resources, and configurations. AI Tools View Access core platform features: * **AI Tools**: Manage your flows, agents, and knowledge bases. * **App Library**: Discover the deployed AI components. * **Templates**: Start quickly with production-grade templates. * **Past Usage**: Navigate all past usage of the platform. * **Task Manager**: Track human-in-the-loop jobs. ## Component Editors Each section within the **AI Tools** (Flows, Agents, and Knowledge Bases) serves as an entry point to its respective visual editor. Clicking on any individual component will open a dedicated workspace where you can design, configure, and test that specific resource in detail. ## Organization Settings To access global configuration and tenant-level management, click the **three dots** menu at the bottom of the sidebar. This opens the **Organization Settings** panel. Organization Settings The settings panel allows you to manage the following areas: * **Profile**: Manage your personal account information and preferences. * **Customize**: Configure organization branding and platform appearance. * **Analytics**: View platform-wide usage metrics and performance data. * **Billing**: Manage subscriptions, payment methods, and invoices. * **Users**: Invite team members and manage organization-level access. * **Workspaces**: Create and organize isolated environments for your projects. * **Model Providers**: Configure global API keys for AI models (OpenAI, Anthropic, etc.). * **Roles**: Define and manage custom permission sets for your team. # Extending the Platform Source: https://docs.noxus.ai/core/platform/extending Custom nodes, integrations, and plugins Noxus is designed to be highly extensible. The platform can be extended using **Plugins**, which allow you to build custom functionality, specialized nodes, and proprietary integrations tailored to your specific requirements. ## Examples Here are some examples of how you can extend the platform using different plugin types: **Create Specialized Nodes** Build custom nodes for domain-specific operations: * Proprietary API integrations * Specialized data transformations * Industry-specific AI operations * Custom business logic ```python theme={null} from noxus_plugins import Node, Input, Output class CustomTransform(Node): name = "My Custom Transform" category = "Data Processing" inputs = [ Input("data", type="string", required=True), Input("config", type="object") ] outputs = [ Output("result", type="string") ] def execute(self, inputs): # Your custom logic result = self.process(inputs['data']) return {"result": result} ``` **Connect Proprietary Systems** Build integrations for internal or specialized systems: * Internal databases and APIs * Legacy system connectors * Proprietary SaaS tools * Custom authentication schemes ```python theme={null} from noxus_plugins import Integration class InternalCRM(Integration): name = "Internal CRM" auth_type = "oauth2" def get_customer(self, customer_id): # Integration logic return self.api_call(f"/customers/{customer_id}") ``` **Custom Model Providers** Integrate proprietary or fine-tuned models: * Self-hosted models * Fine-tuned private models * Custom inference endpoints * On-premises model servers ```python theme={null} from noxus_plugins import ModelProvider class CustomModelProvider(ModelProvider): name = "Internal Models" def generate(self, prompt, model, **kwargs): # Call your model endpoint return self.inference_api.generate(prompt) ``` While the entire Noxus ecosystem is built on a robust, language-agnostic API, we provide a comprehensive toolkit in **Python** and a dedicated **CLI** to streamline the development, testing, and deployment of your extensions. ## Language-Agnostic Development Because Noxus is built on a standardized HTTP/REST architecture, you are not limited to Python for plugin development. Any language capable of hosting an HTTP server (such as Go, Node.js, or Rust) can be used to build a Noxus-compatible plugin. ### HTTP Specification The platform communicates with plugins via a well-defined HTTP interface. By implementing the required endpoints for node discovery and execution, you can integrate your own custom services directly into the Noxus workflow engine. We are currently finalizing the official **Plugin HTTP Specification** documentation. This will include detailed OpenAPI schemas and contract requirements for developers building plugins in non-Python languages. More information will be available soon. *** Check the full guide to learn how to build and deploy custom plugins # Operations Source: https://docs.noxus.ai/core/platform/operations Enterprise observability, scaling, and lifecycle management for Noxus Noxus provides a comprehensive operational framework designed to give you deep visibility into your AI infrastructure and the tools to manage it at scale. ## Observability & Monitoring Noxus leverages industry-standard tools to provide a 360-degree view of your deployment's health and performance. Standardized `/metrics` endpoints across all services provide real-time counters and histograms. Track flow execution rates, worker utilization, and system-wide throughput. Distributed tracing powered by **OpenTelemetry** allows you to follow a single request across the frontend, backend, and worker pools to identify bottlenecks. *** ## Auditability & Compliance Noxus maintains a high-fidelity record of all platform activity, ensuring you can meet strict regulatory and security requirements. ### Platform Audit Logs Every administrative and management action is recorded in a tamper-proof **Audit Log**. This includes: * **Identity**: User ID, email, and API key used for the action. * **Context**: Tenant and Workspace identifiers. * **Action**: The specific operation performed (e.g., `create`, `update`, `delete`, `execute`). * **Resource**: The type and ID of the resource affected (e.g., `workflow`, `agent`, `knowledge_base`). * **Payload**: The request body and metadata associated with the change. ### API & Access Logs Detailed logs of every incoming API call are maintained to track usage patterns and security events: * **Performance**: Request duration (ms) and response codes. * **Routing**: HTTP method and exact route accessed. * **Attribution**: Mapping of every call to a specific user, group, and API key. *** ## Maintenance & Backups Ensure your AI solutions remain available and resilient through automated lifecycle management. Configure scheduled snapshots for your persistence layer (**PostgreSQL**) and **Object Storage**. We recommend a minimum 30-day retention for production environments. Implement multi-region deployment patterns for critical workloads to ensure zero-downtime failover and RTO/RPO compliance. Define data retention rules to automatically move information between high-performance cache and low-cost object storage based on active usage. *** ## Scaling & Resource Management ### Dynamic Worker Scaling Leverage **KEDA** and **HPA** to scale your compute resources based on actual demand: * **Queue-Driven**: Automatically spin up workers as task volume increases and scale-to-zero during idle periods. * **Workload Isolation**: Deploy dedicated worker pools for specific workspaces or high-priority tasks. Explore the full technical guide for monitoring, logging, and scaling your Noxus deployment. # Python SDK Source: https://docs.noxus.ai/core/platform/sdk Full-featured library for AI applications Comprehensive, type-safe Python library for building AI-powered applications. Embed Noxus capabilities directly into your Python projects. ## Key Features Programmatically manage flows, agents, conversations, knowledge bases, and all platform features through a unified Python interface. Full type hints and Pydantic models provide excellent IDE support, autocomplete, and compile-time error checking. Built-in async/await patterns for high-performance, concurrent operations and non-blocking I/O. Real-time streaming of flow executions, conversation responses, and agent actions. ## Common Use Cases Integrate conversational AI, flows, or knowledge base search directly into your Python applications: ```python theme={null} from noxus_sdk.client import Client client = Client(api_key="your_api_key") # Get a flow and run it flow = client.workflows.get("wf_123") run = flow.run( body={"user_query": "Analyze this data"} ) # Query knowledge base results = client.knowledge_bases.search( kb_id="kb_456", query="What's our refund policy?" ) ``` Build custom interfaces, dashboards, or tools on top of Noxus: ```python theme={null} # Create a custom chatbot interface conversation = client.conversations.create( agent_id="agent_789", name="Customer Support Session" ) # Stream responses for chunk in conversation.chat_stream("How do I reset my password?"): print(chunk.content, end="", flush=True) ``` Programmatically create, manage, and execute complex AI flows: ```python theme={null} # Build flow programmatically flow = client.flows.create_builder() flow.add_node("generate_text", { "prompt": "Write a summary", "model": "gpt-4" }) flow.add_node("save_to_file", { "filename": "summary.txt" }) flow.connect("generate_text", "save_to_file") # Deploy and run deployed = flow.deploy("Document Summarizer") result = deployed.run({"document": "..."}) ``` Integrate AI processing into data pipelines and ETL flows: ```python theme={null} import pandas as pd # Process data through Noxus flows df = pd.read_csv("customer_feedback.csv") # Pre-fetch the workflow object flow = client.workflows.get("sentiment_analysis") for idx, row in df.iterrows(): run = flow.run( body={"text": row['feedback']} ) result = run.wait(output_only=True) df.at[idx, 'sentiment'] = result['sentiment'] df.at[idx, 'category'] = result['category'] df.to_csv("analyzed_feedback.csv") ``` Create automation scripts for recurring AI tasks: ```python theme={null} import schedule import time from datetime import datetime # Pre-fetch the workflow object flow = client.workflows.get("daily_analytics") def daily_report(): # Generate daily AI-powered report run = flow.run( body={"date": str(datetime.today())} ) result = run.wait(output_only=True) # Email results send_email(result['report']) schedule.every().day.at("09:00").do(daily_report) while True: schedule.run_pending() time.sleep(60) ``` ## Getting Started ```bash theme={null} pip install noxus-sdk ``` Generate an API key from your Noxus workspace control ```python theme={null} from noxus_sdk.client import Client client = Client(api_key="your_api_key_here") ``` Explore flows, conversations, and knowledge bases Complete SDK documentation with examples and API reference # What is Noxus? Source: https://docs.noxus.ai/core/what-is-noxus Understanding Noxus concepts, platform, and infrastructure ## What is Noxus? Noxus is a comprehensive **AI platform** that brings together **Flows**, **Agents**, and **Knowledge Bases** to create powerful AI-driven solutions. Whether you're a startup moving fast or an enterprise with strict security requirements, Noxus provides the infrastructure, tools, and interfaces needed to build, deploy, and manage AI applications at scale. All in one integrated system. ** For Builders**
Create sophisticated automations using a visual interface or code ** For Enterprises**
Deploy on your infrastructure with complete data control and compliance support ** For Developers**
Integrate via Python SDK, REST API, or extend with custom plugins *** ## Why Noxus? **Complete Platform** - Everything you need to build, deploy, and manage AI applications in one integrated system. **Deployment Flexibility** - Run on our cloud, your infrastructure, or hybrid. Your choice, your control. **Enterprise Ready** - Built for scale with worker pools, GPU support, SSO, audit logs, and compliance features. **Developer Friendly** - Multiple integration paths (visual, SDK, API) with comprehensive documentation. **Open Architecture** - Extend with custom plugins, integrate with any system, deploy on any infrastructure. *** ## Core Concepts The Noxus platform is organized into distinct layers that work together to enable AI automation at scale. ### Organization & Access Organize teams, manage access, and structure your work: Isolated environments with independent resources, settings, and access controls. Organize by team, project, or client. Team members with role-based permissions. Control who can access, create, edit, or administer AI resources. ### AI Tools Building blocks for AI-powered automation. Compose these to create complex agentic flows, multi-agent environments, and sophisticated AI systems: Visual automation combining AI, data processing, and business logic through connected nodes. Conversational AI that uses tools, searches knowledge, and executes other tools autonomously. Semantic search powered by your documents and data. Give AI access to proprietary knowledge. **Analytics & Governance:** All AI tools include comprehensive analytics, observability, and evaluations. Control safety guardrails, governance policies, and compliance at the organization level. ### How Concepts Work Together How Noxus concepts work together **Example workflow:** 1. Create a **Workspace** and invite **Users** with appropriate roles 2. Configure **Models & Providers** for AI access 3. Upload documents to **Knowledge Bases** for semantic search 4. Build **Flows** using visual nodes or code 5. Deploy **Agents** that search knowledge and execute flows 6. Runs execute on **Worker Pools** with isolated compute and monitoring *** ## How to Use Noxus Noxus provides multiple interfaces optimized for different use cases: Drag-and-drop flow builder, agent configuration, and real-time monitoring. Build sophisticated automations without writing code. **Key features:** * Visual node-based workflow builder * Real-time execution monitoring and debugging * Agent configuration with tools and knowledge bases * Knowledge base document management * Team collaboration with role-based access Explore the Visual Platform documentation Full-featured SDK for building AI-powered applications programmatically. Integrate Noxus into your Python applications. ```python theme={null} from noxus_sdk.client import Client client = Client(api_key="your_api_key") # Run a flow result = client.flows.run( flow_id="wf_123", inputs={"query": "Analyze this data"} ) # Chat with an agent conversation = client.conversations.create(agent_id="agent_456") response = conversation.chat("What's our revenue this quarter?") ``` Explore the Python SDK documentation Language-agnostic HTTP API for any platform or language. Access all Noxus features via standard REST endpoints. ```bash theme={null} curl -X POST https://api.noxus.ai/v1/flows/wf_123/run \ -H "Authorization: Bearer ${API_KEY}" \ -H "Content-Type: application/json" \ -d '{"inputs": {"query": "Process this request"}}' ``` **Available endpoints:** * Workflows and runs * Conversations and agents * Knowledge bases and documents * User and workspace management Explore the REST API documentation ### Plugin extensions Extend the platform with custom nodes, integrations, and specialized AI models. Build proprietary functionality and share across your organization. **What you can build:** * Custom nodes for proprietary systems * Integrations for internal tools * Specialized AI models and processors * Reusable components for your team Explore plugin development documentation *** ## Deployment Flexibility Noxus adapts to your infrastructure and security requirements: Fully managed platform with zero infrastructure overhead. Get started in minutes. Deploy on your infrastructure with complete data control and customizable architecture. Combine cloud management with on-premises execution for optimal flexibility. Secure deployments in completely isolated environments without internet connectivity. ### Enterprise Features * **Worker pool isolation** - Separate processing by team, project, or security requirement * **GPU support** - Dedicated GPU workers for AI-intensive operations * **Network isolation** - Run in isolated networks or VPCs with complete control * **Data residency** - Keep data within specific geographic regions * **SSO & custom auth** - SAML, OAuth, or custom authentication providers * **Compliance ready** - HIPAA, SOC 2, GDPR support * **Audit logging** - Comprehensive activity logs for security and compliance * **High availability** - Multi-region deployment with automatic failover Explore deployment options and architecture details *** ## Next Steps Create a free account and start building AI flows in minutes Deep dive into flows, agents, and knowledge bases Understand deployment options and infrastructure requirements Integrate Noxus into your applications *** ## Need Help? Get help from our team with setup, deployment, or usage questions See Noxus in action with a personalized demo # Architecture Source: https://docs.noxus.ai/deployment/architecture Noxus runtime architecture for on-premise or on-cloud deployments Noxus has a flexible runtime topology that is kept consistent on the different deployment methods, based on a few key services. The following graph provides a quick overview of this topology: ```mermaid theme={null} flowchart TB subgraph Edge[Public Edge] DNS[DNS] IN[Ingress / Reverse Proxy] end subgraph App[Noxus Application] FE[Noxus Frontend] BE[Noxus Backend] RE[Noxus Relays] W[Noxus Workers] SB[Agent Sandbox] end subgraph Data[Data Services] PG[(PostgreSQL + pgvector)] RD[(Redis)] CS[(Cold Storage)] end subgraph Obs[Observability] PM[Prometheus] OT[OpenTelemetry Collector] LG[Central Logs] end DNS --> IN IN --> FE IN --> BE IN --> RE FE --> BE BE --> PG BE --> RD BE --> CS W --> PG W --> RD W --> CS W --> SB SB --> CS RE --> PG RE --> RD FE -.metrics.-> PM BE -.metrics.-> PM W -.metrics.-> PM RE -.metrics.-> PM BE -.traces.-> OT W -.traces.-> OT RE -.traces.-> OT FE -.logs.-> LG BE -.logs.-> LG W -.logs.-> LG RE -.logs.-> LG ``` ## Core Noxus Components Next.js application that serves the user interface and talks to Noxus Backend over API/WebSocket. API/control plane for workflows, auth, configuration, integrations, and orchestration. Async execution plane for runs. In Kubernetes, workers are pool-based (`worker.pools`) with HPA or KEDA autoscaling. Optional integration webhook/event handlers (for external events and relay endpoints). Off by default. Isolated execution for Run Code nodes and **all plugin execution**. Off by default; the plugin system is disabled without it. See [Agent Sandbox](/deployment/sandbox). ## Data Layers * **PostgreSQL**: domain entities, analytics, platform audit logs, embeddings (pgvector), **and the job queue** * **Redis**: cache, distributed locks, and live-stream coordination * **Cold Storage**: object/file persistence (`s3`, `gcloud`, `minio`, local-compatible options) ### The job queue lives in PostgreSQL Workers poll a PostgreSQL-backed queue rather than consuming from a message broker. Two consequences worth planning around: * **Database connection count scales with worker replicas.** Each Backend and Worker pod opens its own connection pools — `POSTGRES_POOL_SIZE` (default 64) plus a separate vector pool and a queue pool. A deployment with 2 Backend and 10 Worker replicas can request well over a thousand connections before any user traffic. Run a connection pooler; the Helm chart bundles PgBouncer for this. * **Backlog is directly observable**, which is what makes queue-driven autoscaling accurate. KEDA reacts to actual queue depth instead of waiting for CPU to rise after work has already been sitting. Redis losing its data costs a cold cache and any in-flight stream — not durable state. Configure it with `allkeys-lru` eviction so it degrades under memory pressure rather than erroring on write. ## Traffic And Domains The platform default ingress model uses three entrypoints for the platform. As an example: * `` -> Noxus Frontend * `api.` -> Noxus Backend * `relay.` -> Noxus Relays (if enabled) These however can be mapped differently, in case of limitations such as: * `` -> Noxus Frontend * `/api/backend` -> Noxus Backend * `/relays` -> Noxus Relays (if enabled) ### Long-lived connections Agent responses stream over SSE and WebSocket connections held open for minutes. Default proxy read timeouts are 60 seconds, which truncates responses mid-answer. Raise the timeout on whatever terminates TLS — load balancer idle timeout, `proxy-read-timeout` on nginx, a `BackendConfig` on GKE — and check any CDN or WAF in front, which applies its own limit regardless of the origin setting. Entry point to deployment models, config, security, and operations Helm-based architecture with worker pools, KEDA, and ingress Isolated code execution, required for plugins Terraform modules, Helm chart, and operational runbooks # Serverless Containers Source: https://docs.noxus.ai/deployment/cloudrun/overview Deploy Noxus on managed container runtimes (Cloud Run, Container Apps) using Terraform This option runs Noxus services as managed containers while leveraging cloud-native managed data services. This architecture provides high availability and automatic scaling with reduced operational overhead. ## Supported Platforms **Terraform** configurations live in the [noxus-infra](https://github.com/noxus-ai/noxus-infra) repository. Available today. Cloud Run, Cloud SQL (PostgreSQL), Memorystore (Redis), and Cloud Storage. **In progress.** The target architecture is documented in the repository, but the Terraform is not yet published. For Azure today, use the [Kubernetes](/deployment/kubernetes/overview) path on AKS. *** ## Infrastructure as Code The Terraform handles container services, networking, IAM, and managed data services. ### Deployment Steps ```bash theme={null} git clone https://github.com/noxus-ai/noxus-infra cd noxus-infra/terraform/stacks/serverless/gcp-cloudrun ``` ```bash theme={null} cp terraform.tfvars.example terraform.tfvars ``` Fill in your project, domain, admin email, platform version, and Auth0 application credentials. There are no default Auth0 values — you must supply your own tenant. ```bash theme={null} tofu init && tofu apply ``` The stack outputs `frontend_dns`, `backend_dns`, and `relay_dns`. Create the matching records before the managed certificates can issue. *** ## Recommended Service Split The platform is split into several independent container services to allow for granular scaling and resource allocation. ```mermaid theme={null} flowchart TB IN[Public Ingress] --> FE[Noxus Frontend Container] IN --> BE[Noxus Backend Container] IN --> RE[Noxus Relays Container] BE --> W[Noxus Workers Container] BE --> PG[(Managed PostgreSQL + pgvector)] W --> PG BE --> RD[(Managed Redis)] W --> RD RE --> RD W --> CS[(Managed Object Storage)] BE --> CS ``` *** ## Practical Notes Workflow runs and knowledge-base ingestion are long-running and outlive request timeouts. Workers therefore run as **always-on instances with a fixed replica count**, not scale-to-zero — which removes most of the serverless cost argument for the platform's largest component. If knowledge-base ingestion is central to your workload, prefer [Kubernetes](/deployment/kubernetes/overview), where dedicated worker pools keep ingestion from affecting agent latency. The `beat` service runs scheduled jobs. Pin it to **exactly one** replica — two schedulers double-enqueue every scheduled run. You get separate services, not pools with distinct queue subscriptions and independent autoscaling. Per-workspace worker isolation is a Kubernetes-only capability. Agent responses hold a connection open for minutes. Default request timeouts truncate them mid-response — raise the timeout on the container service's ingress. Only enable Noxus Relays if you need webhook/event receiver endpoints for external integrations. Deploy the sandbox as an additional internal service and point `SANDBOX_MANAGER_URL` at it. Without it the plugin system is silently disabled — see [Agent Sandbox](/deployment/sandbox). *** ## Configuration Strategy * **Secrets Management**: Inject sensitive credentials (database passwords, API keys) via cloud-native secret bindings (GCP Secret Manager or Azure Key Vault). * **Environment Variables**: Keep non-sensitive configuration in the platform's environment variables. * **Service Naming**: Maintain consistent names (`noxus-frontend`, `noxus-backend`, etc.) across all environments to simplify monitoring and operations. Shared variable model across deployment options. Access Terraform modules and deployment scripts. # Environment Source: https://docs.noxus.ai/deployment/configuration/environment How Noxus environment configuration is structured across deployment models Noxus configuration is split between: * **runtime env vars** (URLs, deployment mode, non-sensitive settings) * **secrets** (credentials and keys) * **admin-managed platform settings** (global configuration in the Noxus admin portal) ## Configuration Sources ```mermaid theme={null} flowchart LR GIT[Helm values / Compose / IaC] --> ENV[Runtime Env Vars] SM[Secret Manager / K8s Secret / Env files] --> SEC[Secrets] ADM[Noxus Admin Portal] --> CFG[server_settings and auth config] ENV --> APP[Noxus services] SEC --> APP CFG --> APP ``` ## Environment Variable Layers | Layer | Example | | -------------------- | --------------------------------------------------- | | Base platform env | deployment env, URLs, storage mode, feature toggles | | Service-specific env | frontend-only host binding, worker subscribe mode | | Secrets | DB/Redis credentials, auth keys, provider secrets | ## Environment Variable Reference ### Platform | Variable | Default | Description | | ----------------- | --------- | ------------------------------------------------------------------- | | `DEPLOYMENT_ENV` | `"prod"` | Environment name for logging purposes | | `DEPLOYMENT_NAME` | `"NOXUS"` | Display name for the deployment instance | | `ON_PREM` | `"false"` | Set to `"true"` for non-cloud based deployments | | `ADMIN_EMAIL` | — | Admin email used during initial bootstrap - only used on first boot | ### URLs | Variable | Default | Description | | -------------- | ------- | ----------------------------------------------------------------------------------------------------------------------- | | `BACKEND_URL` | — | Public URL for the backend API (e.g. `https://api.example.com`). Can be set to an internal LB for the frontend instance | | `FRONTEND_URL` | — | Public URL for the frontend (e.g. `https://example.com`) | | `RELAY_URL` | — | Public URL for the relay service (e.g. `https://relay.example.com`) | ### Database Configuration | Variable | Default | Description | | --------------------------- | -------- | --------------------------------------------- | | `DATABASE` | `"spot"` | PostgreSQL database name | | `POSTGRES_POOL_SIZE` | `"64"` | Connection pool size for the primary database | | `VECTOR_POSTGRES_POOL_SIZE` | `"128"` | Connection pool size for the vector database | | `RUN_MIGRATIONS` | `"1"` | Run Alembic migrations on startup | ### Redis | Variable | Default | Description | | ---------------------- | --------- | --------------------------------------------------------------------------------------- | | `REDIS_PORT` | `"6379"` | Redis server port | | `REDIS_SSL` | `""` | Enable SSL for Redis connections | | `REDIS_SINGLE_DB_ONLY` | `""` | Restrict to a single Redis database, for compatibility with some managed Redis services | | `CACHE_LOCATION` | `"redis"` | Cache backend location | ### Object Storage | Variable | Default | Description | | ----------------------- | ------------- | -------------------------------------------- | | `BUCKET_CLIENT` | `"s3"` | Storage provider: `gcloud`, `s3`, or `minio` | | `STORAGE_BUCKET` | — | Primary storage bucket name | | `PUBLIC_STORAGE_BUCKET` | — | Public assets bucket name | | `S3_ENDPOINT_URL` | `""` | Custom S3 endpoint (leave empty for AWS S3) | | `AWS_REGION` | `"eu-west-1"` | AWS region for S3 operations | ### Observability | Variable | Default | Description | | -------------------------- | -------- | --------------------------------------------- | | `OTEL_COLLECTOR_ENDPOINT` | `""` | OpenTelemetry collector endpoint | | `PROMETHEUS_MULTIPROC_DIR` | `"/tmp"` | Directory for Prometheus multiprocess metrics | | `PROM_REMOTE_WRITE_URL` | `""` | Prometheus remote write endpoint | | `ENABLE_LLM_OBSERVABILITY` | `"true"` | Enable LLM call tracing in OTEL | ### Worker Configuration (per deployment) | Variable | Default | Description | | ------------------------------- | -------------- | --------------------------------------------------------------- | | `WORKER_SUBSCRIBE` | `"all_but_kb"` | Queue type: `all`, `all_but_kb`, `flow`, `chat`, `kb` | | `WORKER_UNSUBSCRIBE_WORKSPACES` | `""` | Comma-separated workspace IDs to exclude (empty = exclude none) | | `WORKER_SUBSCRIBE_WORKSPACES` | `""` | Comma-separated workspace IDs to process (empty = all) | ### Agent Sandbox & Plugins | Variable | Default | Description | | ------------------------ | ------- | --------------------------------------------------------------------------------------------------------------------------------------------- | | `SANDBOX_MANAGER_URL` | `""` | URL of the agent sandbox manager. Plugins execute inside sandboxes, so this also enables the plugin system — **empty disables it, silently**. | | `USE_REMOTE_SANDBOX` | `true` | Route Run Code through the sandbox service rather than executing locally. | | `REMOTE_SANDBOX_BACKEND` | `""` | Remote execution backend. `syd` in normal use. | | `SANDBOX_BACKEND` | `syd` | Jail implementation: `syd` (syscall + network jail, needs `SYS_PTRACE`) or `minimal` (chroot only). | | `LOCAL_SANDBOX_BACKEND` | `deno` | Worker-local fallback: `none`, `deno`, or `subprocess`. | | `PLUGIN_CALL_TIMEOUT` | `240.0` | Seconds before a plugin call is abandoned. | `LOCAL_SANDBOX_BACKEND=subprocess` runs user code in the Worker's own process tree — with its filesystem, network, and environment, including database credentials. Do not use it in a multi-tenant deployment. See [Agent Sandbox](/deployment/sandbox) for the deployment model and platform compatibility. *** ## Deployment-Independent Principles * Keep non-sensitive settings in environment config * Keep credentials in secrets only * Keep environment names simple (`local`, `staging`, `prod`) * Do not expose internal-only controls (such as billing internals) in user-facing docs Noxus supports extensive runtime configuration from the admin portal when the user has global admin permissions. This includes global server settings and auth behavior. ## Practical Mapping In Your Stack * VM compose: `env_file` and explicit env mounts * Helm: `env`, `extraEnv`, `secrets`, plus service-specific secret variants * Terraform stage3: secret/env materialization and namespace-scoped injection Secret handling, provider credentials, and worker secret injection Worker pools, task routing, workspace isolation, and autoscaling Isolated code execution, required for plugins Object storage, vector databases, and caching layers PostgreSQL and pgvector requirements, and the runtime topology # Rate Limiting Source: https://docs.noxus.ai/deployment/configuration/rate-limiting How the platform throttles API requests, and how to tune the limits The platform rate-limits requests to protect the backend from overload and abuse. Limits are enforced in Redis with a sliding window and are **configurable per endpoint group** — you can tune them without a code change. ## How it works * Each protected endpoint belongs to a **rate-limit group** (for example `create_run_async`, `kb_search`, `sandbox_create`, `tables_write`, `deployments_write`, or `default`). * A group's limit is a **rule**: *N requests per time window* (`runs` per `delta_seconds`). * The counter is keyed by the caller — the **API key's workspace** for API-key endpoints, or the **user** for authenticated app endpoints — so one tenant's traffic never consumes another's budget. * When a caller exceeds the limit, the request is rejected with **HTTP 429** and a **`Retry-After`** header telling the client how long to wait. Limits are stored in the database and cached, so changes take effect without a redeploy. The in-code defaults are the fallback used if the stored config can't be read. ## Tuning the limits Tenant admins configure the limits in the app under **Settings → Platform → Rate limits**. Each endpoint group shows its current allowance; adjust the number of requests and the window per group. Typical groups you'll see: | Group | Guards | Why it's separate | | -------------------------------------- | ----------------------------------------------- | ----------------------------------------------- | | `create_run_async` / `create_run_sync` | Starting workflow runs | Runs are expensive; sync runs hold a connection | | `kb_search`, `upload_document` | Knowledge-base search / ingestion | Vector search and ingestion are heavy | | `sandbox_create` / `sandbox_exec` | Creating sandboxes vs. running commands in them | Booting a sandbox is much heavier than exec | | `tables_query` / `tables_write` | Table SQL vs. writes | Arbitrary SQL is the expensive path | | `deployments`, `triggers`, `insights` | Channel/trigger/analytics reads and writes | Scoped so a burst on one doesn't starve others | | `default` | Everything without a dedicated group | Catch-all baseline | Raise the groups your integration hammers (e.g. `tables_query` for a reporting job) rather than a blanket increase — the per-group split exists so one hot path can't exhaust the whole budget. ## Handling 429 in clients Any client should treat a `429` as "back off and retry after the `Retry-After` interval." The [Python SDK](/sdk/concepts/authentication) already does this for you — it retries `429`s with bounded exponential backoff that honors `Retry-After`, and raises `RateLimitedError` only after exhausting its retries. In other languages, read the `Retry-After` header and retry rather than hammering. Rate limiting is one layer. Front the platform with your own WAF / gateway for IP-level protection and DDoS mitigation — see [Inbound networking](/deployment/networking/inbound). # Agent Sandbox Source: https://docs.noxus.ai/deployment/configuration/sandbox Deploy and configure the sandbox that runs agent code, code-execution nodes, and plugins Anything on the platform that runs code — the agent **Sandbox** and **Code Execution** tools, code-execution flow nodes, plugin nodes, and the [`/v1/sandboxes` API](/api-reference/v1--sandboxes/create-sandbox) — executes inside an isolated **sandbox**. As an operator you choose which sandbox **backend** runs that code, trading isolation strength against infrastructure requirements. ## Backends The platform picks a backend in two layers: a **remote** sandbox-manager service (preferred), falling back to a **local** in-worker backend when the remote is off, unhealthy, or unreachable. The remote sandbox manager runs each sandbox as a **gVisor** (`runsc`) container — a user-space kernel that intercepts syscalls, with OverlayFS and a jailed network namespace. Strong isolation; runs in Docker / Cloud Run / Kubernetes. The remote manager can instead run each sandbox as a full **cloud-hypervisor micro-VM**. The strongest isolation, but **requires KVM** — bare-metal or nested-virtualization hosts only (not Docker). An in-process **Deno** sandbox with a SQLite-backed filesystem. No Docker, no separate service — good for local dev and lightweight deployments. subprocess runs code in a worker-local Python subprocess (least isolation — dev only). none disables local execution entirely. `subprocess` provides no real isolation — untrusted code runs with the worker's privileges. Use it only in trusted local development. For any shared or production deployment, run the **remote sandbox manager** (gVisor or MicroVM). ## Choosing a backend | You're running… | Recommended backend | | ----------------------------------------------------- | --------------------------------------------- | | Production / multi-tenant | Remote **gVisor** (`USE_REMOTE_SANDBOX=true`) | | Bare-metal / KVM host wanting VM-grade isolation | Remote **MicroVM** | | Local development | Local **deno** (default) | | A worker with no sandbox service and Deno unavailable | **subprocess** (trusted only) or **none** | ## Configuration The behavior is driven by environment variables read from the worker/backend settings: | Variable | Default | Purpose | | ------------------------ | ------- | ----------------------------------------------------------------------------------------------------- | | `USE_REMOTE_SANDBOX` | `true` | Prefer the remote sandbox-manager service. | | `REMOTE_SANDBOX_BACKEND` | `syd` | The remote backend type. (`SANDBOX_BACKEND` is a deprecated alias; `auto`/`deno` normalize to `syd`.) | | `LOCAL_SANDBOX_BACKEND` | `deno` | Local path when the remote is off/unhealthy: `deno`, `subprocess`, or `none`. | | `SANDBOX_MANAGER_URL` | — | URL of the remote sandbox-manager service. Required when `USE_REMOTE_SANDBOX=true`. | | `ADMIN_API_KEY` | — | Shared secret the platform sends to the manager (`X-API-Key`). **Set a strong value in production.** | When `USE_REMOTE_SANDBOX=true` but `SANDBOX_MANAGER_URL` is unset or the manager is unhealthy, the platform automatically falls back to `LOCAL_SANDBOX_BACKEND`, so code execution keeps working (with weaker isolation). Set `LOCAL_SANDBOX_BACKEND=none` if you'd rather fail closed. ## Deploying the remote sandbox manager The remote backends are served by a separate **sandbox-manager** service (the `agentsandbox` component): * **gVisor manager** — a container image bundling `runsc` (gVisor) and a minimal Debian rootfs; exposes an HTTP control-plane on port **8000**. Deployable in Docker / Cloud Run / Kubernetes. * **MicroVM manager** — runs on a KVM-capable host using cloud-hypervisor; HTTP control-plane on port **8400**. Point the platform at it with `SANDBOX_MANAGER_URL` and share the `ADMIN_API_KEY`. Every control-plane call is authenticated with that key via the `X-API-Key` header, so the manager is never exposed unauthenticated. ## Security model Regardless of runtime, sandboxes are built to contain untrusted code: * **Isolation** — gVisor intercepts syscalls in a user-space kernel; MicroVM uses a full guest kernel. Each sandbox gets its own filesystem (OverlayFS: read-only base + per-sandbox writable layer). * **Network jail** — private/internal ranges (RFC1918) are blocked from inside the sandbox; it can NAT out to the public internet but cannot reach internal services. (This is why the [SDK-in-sandbox](/sdk/resources/sandboxes) pattern needs a publicly reachable backend URL.) * **Ephemeral** — non-persistent sandboxes are cleaned up when idle. * **Permission-gated** — the `sandboxes:run` permission is required to run code and is never implied by general resource access. Related reading: the [Agent Sandbox](/platform/agents/sandbox) product page (how agents use it) and the [Sandboxes SDK](/sdk/resources/sandboxes) / [Sandboxes API](/api-reference/v1--sandboxes/create-sandbox) for programmatic use. # Secrets Source: https://docs.noxus.ai/deployment/configuration/secrets Secret management for Noxus services, model providers, and worker execution Secret management in Noxus is designed to separate platform-level infrastructure credentials from workspace-specific runtime secrets. ## What Belongs In Secrets * **Infrastructure**: Database and Redis credentials, storage access keys. * **Identity**: Auth provider (OIDC/SAML) client secrets and signing keys. * **AI Providers**: Global API keys for model providers (OpenAI, Anthropic, etc.). * **Integrations**: Credentials for third-party tools used across the platform. ## Secret Reference ### Database | Secret | Description | | ----------------------- | ---------------------------------------------------------------- | | `POSTGRESQL_URL` | Primary PostgreSQL connection string (`user:password@host:5432`) | | `VECTOR_POSTGRESQL_URL` | Vector database (pgvector) connection string | ### Redis | Secret | Description | | ---------------- | --------------------------------------------- | | `REDIS_URL` | Redis host (without port) | | `REDIS_PASSWORD` | Redis authentication password | | `REDIS_USER` | Redis username (optional, for ACL-based auth) | ### Object Storage | Secret | Description | | ---------------------- | -------------------------------- | | `S3_ACCESS_KEY_ID` | S3-compatible storage access key | | `S3_SECRET_ACCESS_KEY` | S3-compatible storage secret key | ### Observability | Secret | Description | | ---------------------------- | ------------------------------------------- | | `PROM_REMOTE_WRITE_USERNAME` | Prometheus remote write basic auth username | | `PROM_REMOTE_WRITE_PASSWORD` | Prometheus remote write basic auth password | ### Integrations | Secret | Description | | ------ | ----------- | *** ## Common Secret Backends Noxus supports several backends for storing and injecting secrets depending on your deployment model. **Native Secrets & External Secret Operators** - Store values in Kubernetes `Secrets` within the relevant namespaces. - Inject via `envFrom` for all services or explicit `secretKeyRef` for specific containers. - Support for **External Secrets Operator** to sync from AWS Secrets Manager, HashiCorp Vault, or Azure Key Vault. **Managed Secret Managers** - **AWS Secrets Manager / Parameter Store**: Inject via IAM roles for service accounts (IRSA). - **GCP Secret Manager**: Access via Workload Identity and direct environment binding. - **Azure Key Vault**: Integration via Managed Identities and CSI drivers. **Encrypted Files & Environment** - Use protected `.env` or `/env.vm` files with `0600` filesystem permissions. - Support for **HashiCorp Vault** as a centralized sidecar or initialization agent. - Systemd-level environment protection for the `noxus` service. *** ## Secret Injection Strategies Noxus allows for granular control over where secrets are exposed within the platform architecture. ### Global Platform Secrets These are injected into **all services** (Frontend, Backend, Workers). * **Use Case**: Database connection strings, Redis URLs, and core authentication secrets. * **Implementation**: Defined in the primary Helm values or the main `/env.vm` file. ### Worker-Only Secrets Inject secrets exclusively into the **Noxus Workers**. * **Use Case**: Proprietary API keys for custom nodes, database strings for specific ETL flows, or credentials for internal tools. * **Benefit**: Limits the "blast radius" by ensuring the web-facing frontend and backend never have access to these sensitive runtime credentials. ### Pool-Specific Secrets (Kubernetes) For advanced isolation, secrets can be injected into **specific worker pools** using `envSecretRef`. * **Use Case**: A tenant-dedicated worker pool that needs its own LLM API keys, while the shared pool uses the platform defaults. * **Implementation**: Set `worker.pools[].envSecretRef` in Helm values to the name of an additional K8s Secret. It is mounted after the shared secret, so its values override any overlapping keys. See the [Workers configuration guide](/deployment/configuration/workers#per-pool-secrets) for detailed examples. Secrets injected into the worker environment are accessible from within the platform itself. Any workflow node, agent tool, or custom plugin running on a worker can read environment variables using the `{{secret.MY_SECRET_NAME}}` syntax in node configuration fields. This means you can add custom secrets (e.g., a third-party API key) to a worker's environment and reference them directly from flows and agents without hardcoding credentials in the UI. When using pool-specific secrets via `envSecretRef`, only workers in that pool will have access to those values — other pools and services will not see them. *** ## Rotation & Security Guidelines * **Regular Rotation**: Rotate high-risk credentials (like API keys) on a 30-90 day schedule. * **Least Privilege**: Only inject secrets into the specific services or worker pools that require them. * **Automated Reloads**: In Kubernetes, use tools like `reloader` to automatically restart pods when their underlying secrets are updated. Learn how to configure pool-specific secrets in K8s. Understand the broader security context of the platform. # Storage Source: https://docs.noxus.ai/deployment/configuration/storage The three storage layers of the Noxus platform: Persistence, Cache, and Object Storage Noxus utilizes a multi-tier storage architecture to ensure data integrity, high-performance coordination, and scalable artifact management. ## 1. Persistence Layer (PostgreSQL) PostgreSQL serves as the primary source of truth for the platform, managing all structured data and knowledge metadata. * **Requirements**: PostgreSQL v15+ with the `pgvector` extension. * **Core Data**: User accounts, workspace configurations, flow definitions, agent settings, and knowledge base metadata. * **Vector Search**: The `pgvector` extension enables high-performance semantic search and retrieval for AI knowledge bases. * **Vector Database Flexibility**: While `pgvector` is the default for platform-native Knowledge Bases, Noxus can be configured to support other common vector databases. Additionally, workflows and agents can connect to any externally hosted vector database during runtime for specialized search requirements. * **Operational Tip**: Use managed services like AWS RDS or GCP Cloud SQL with automated backups and multi-AZ failover for production environments. ## 2. Coordination Layer (Task Broker) This layer powers real-time task queueing and message passing between Noxus services for asynchronous execution. * **Default Engine**: **Redis** is the default engine for asynchronous flow execution and service coordination. * **Alternative Engines**: This layer is highly configurable to match your infrastructure: * **PostgreSQL**: Can be used as the coordination engine for simpler, single-database architectures. * **RabbitMQ**: Supported for high-throughput, enterprise-grade message queueing requirements. * **Security**: **Never expose the coordination layer to the public internet.** Ensure it is only accessible via private networking from Noxus services. ## 3. Caching Layer The caching layer is dedicated to accelerating access to frequently used resources and transient state. * **Technology**: **Redis** is always utilized as the high-speed caching layer. * **Usage**: Stores session data, frequently accessed configuration, and temporary computation results to minimize latency. * **Operational Tip**: Monitor memory saturation and eviction policies to ensure optimal performance during high-load bursts. ## 4. Object Storage Layer (S3/GCS/MinIO) The object storage layer handles large-scale binary data, files, and artifacts generated or consumed by your AI workflows. * **Supported Backends**: S3-compatible storage (AWS S3, Google Cloud Storage, Azure Blob Storage, or on-premises MinIO). * **Core Data**: Uploaded documents for knowledge bases, generated images/files from flows, and archived execution payloads. * **Lifecycle Management**: Implement retention rules to automatically archive or delete old artifacts based on your organization's data policy. * **Security**: Enforce server-side encryption and use IAM roles/Service Accounts for access instead of long-lived access keys. *** ## Liquid Data Management Noxus features a **Liquid Data** architecture that autonomously manages the lifecycle of your information across these storage tiers. * **Autonomous Movement**: Data is automatically shifted between high-performance cache, persistence, and cold storage based on active usage patterns and access frequency. * **Policy-Driven Retention**: Define granular retention policies that govern how long data remains in each tier before being archived or purged. * **Budget Optimization**: By intelligently moving inactive data to lower-cost object storage, Noxus helps you maintain strict storage budgets without sacrificing performance for active workloads. *** ## Storage Architecture Summary | Layer | Technology | Primary Function | | :----------------- | :-------------------------- | :---------------------------------------------- | | **Persistence** | PostgreSQL + `pgvector` | Source of truth, metadata, and semantic search. | | **Coordination** | Redis / Postgres / RabbitMQ | Task queueing and service orchestration. | | **Caching** | Redis | High-speed data access and session state. | | **Object Storage** | S3 / GCS / MinIO | Large files, artifacts, and knowledge assets. | Learn how to protect your data across all three storage layers. See how to configure connection strings for each storage backend. # Workers Source: https://docs.noxus.ai/deployment/configuration/workers Configure worker pools, task routing, tenant isolation, and autoscaling for Noxus deployments Noxus workers execute background tasks — workflow runs, knowledge-base ingestion, and agent conversations. The job queue lives in **PostgreSQL**. Workers poll it rather than consuming from a broker, which has two practical consequences: * **Database connection pressure scales with worker replica count.** Each pod opens its own connection pools. See [connection pooling](#connection-pooling) below. * **Queue depth is directly observable in the database**, which is what makes KEDA's queue-driven autoscaling accurate — it reacts to actual backlog instead of waiting for CPU to rise after work has already been queued. Redis is used for caching, distributed locks, and live-stream coordination — not as the job queue. Workers are configured through environment variables that control task routing, and the Helm chart supports defining **multiple pools** — each with its own Deployment, Service, autoscaler, and PodDisruptionBudget. ## Task Routing ### Queue Types (`WORKER_SUBSCRIBE`) Each worker pool subscribes to one or more task types via the `WORKER_SUBSCRIBE` environment variable. | Queue Type | Description | | ------------ | ------------------------------------------ | | `all` | Process all task types | | `all_but_kb` | Everything except knowledge-base ingestion | | `flow` | Workflow execution only | | `chat` | Conversational AI / agent tasks only | | `kb` | Knowledge-base ingestion only | A single pool on the default `all_but_kb` has **no consumer for knowledge-base ingestion** — those jobs queue indefinitely with nothing to pick them up. Either add a `kb` pool or set your single pool to `all`. ### Workspace Filtering Workers can be scoped to specific workspaces using comma-separated ID lists: | Variable | Description | | ------------------------------- | ------------------------------------------------------------- | | `WORKER_SUBSCRIBE_WORKSPACES` | Workspace IDs this worker processes (empty = all) | | `WORKER_UNSUBSCRIBE_WORKSPACES` | Workspace IDs this worker **excludes** (empty = exclude none) | These filters combine with `WORKER_SUBSCRIBE` — a pool set to `workerSubscribe: "flow"` with `workerSubscribeWorkspaces: "ws-abc,ws-xyz"` processes workflow tasks for those two workspaces only. The pair is designed to be used together: give a noisy workspace a dedicated pool via `workerSubscribeWorkspaces`, and add the same IDs to the catch-all pool's `workerUnsubscribeWorkspaces` so it keeps running everything else — including maintenance and system jobs — without competing for that workspace's work. *** ## Worker Pools Define multiple pools under `worker.pools` in your Helm values. Each pool creates an independent Kubernetes Deployment. ### Pool Configuration Reference | Field | Type | Default | Description | | ----------------------------- | ------ | -------------- | ---------------------------------------------------------------------- | | `enabled` | bool | — | Enable or disable this pool | | `replicaCount` | int | `1` | Static replica count (ignored when autoscaling is enabled) | | `workerSubscribe` | string | `"all_but_kb"` | Queue type subscription | | `workerSubscribeWorkspaces` | string | `""` | Workspace IDs this pool processes (empty = all) | | `workerUnsubscribeWorkspaces` | string | `""` | Workspace IDs this pool excludes (empty = exclude none) | | `envSecretRef` | string | `""` | Name of an additional K8s Secret to layer on top of the shared app-env | | `resources` | object | — | CPU/memory requests and limits | | `autoscaling` | object | — | HPA or KEDA autoscaling config | | `podDisruptionBudget` | object | — | PDB settings | | `affinity` | object | `{}` | Pod affinity/anti-affinity rules | | `nodeSelector` | object | `{}` | Node selector constraints | | `tolerations` | list | `[]` | Node tolerations | | `topologySpreadConstraints` | list | `[]` | Topology spread rules | ### Basic Multi-Pool Example ```yaml theme={null} worker: enabled: true pools: default: enabled: true workerSubscribe: "all_but_kb" resources: requests: cpu: "2" memory: "12Gi" limits: cpu: "2" memory: "12Gi" autoscaling: enabled: true type: "keda" minReplicas: 1 maxReplicas: 10 kb: enabled: true workerSubscribe: "kb" resources: requests: cpu: "4" memory: "16Gi" limits: cpu: "4" memory: "16Gi" autoscaling: enabled: true type: "hpa" minReplicas: 1 maxReplicas: 5 targetCPUUtilizationPercentage: 75 chat: enabled: true workerSubscribe: "chat" resources: requests: cpu: "4" memory: "16Gi" limits: cpu: "4" memory: "16Gi" autoscaling: enabled: true type: "keda" minReplicas: 0 maxReplicas: 5 keda: query: >- SELECT COUNT(*) FROM runs WHERE status IN ('Queued') AND queue_type = 'chat'; targetQueryValue: "5" ``` *** ## Workspace Isolation Use `workerSubscribeWorkspaces` and `workerUnsubscribeWorkspaces` to dedicate worker pools to specific workspaces. This is useful for: * **Noisy-neighbor isolation** — prevent one team's heavy workloads from starving others * **SLA tiers** — dedicated capacity for priority workspaces * **Data residency** — pin certain workspaces to workers on specific nodes or in specific zones The two variables are designed to be used as a pair. Giving a workspace a dedicated pool is only half the job: without excluding it from the catch-all pool, both pools compete for the same work. ```yaml theme={null} worker: pools: # Catch-all pool. Runs everything except the workspaces that have their own # pool below — including maintenance and system jobs. default: enabled: true workerSubscribe: "all" workerUnsubscribeWorkspaces: "ws_acme_001,ws_ent_001,ws_ent_002" autoscaling: enabled: true type: "keda" minReplicas: 1 maxReplicas: 10 # Dedicated pool for a high-volume workspace workspace-acme: enabled: true workerSubscribe: "all" workerSubscribeWorkspaces: "ws_acme_001" autoscaling: enabled: true type: "keda" minReplicas: 1 maxReplicas: 8 # Dedicated KB processing for specific workspaces kb-enterprise: enabled: true workerSubscribe: "kb" workerSubscribeWorkspaces: "ws_ent_001,ws_ent_002" autoscaling: enabled: true type: "hpa" minReplicas: 1 maxReplicas: 5 targetCPUUtilizationPercentage: 75 ``` This isolates **load**, not data. All pools share one database and one cluster; the tenant and workspace boundaries are enforced by the application, not by the infrastructure. If you need infrastructure-level separation between customers, run separate deployments. *** ## Per-Pool Secrets By default, all worker pools share the same Kubernetes Secret (`{release}-app-env`). When different pools need different environment variables — such as separate LLM API keys per tenant, different Redis databases, or pool-specific feature flags — use `envSecretRef` to layer an additional Secret on top. ```yaml theme={null} worker: pools: default: enabled: true workerSubscribe: "all" # Uses only the shared app-env secret tenant-acme: enabled: true workerSubscribe: "all" workerSubscribeWorkspaces: "ws_acme_001" # Env vars in this secret override the shared app-env envSecretRef: "acme-worker-env" ``` Create the per-pool secret separately (or via External Secrets Operator): ```yaml theme={null} apiVersion: v1 kind: Secret metadata: name: acme-worker-env namespace: spotflow type: Opaque stringData: OPENAI_API_KEY: "sk-acme-dedicated-key" ANTHROPIC_API_KEY: "sk-ant-acme-key" ``` The pool-specific secret is mounted **after** the shared one in `envFrom`, so its values take precedence for any overlapping keys. *** ## Multi-Namespace Deployment To run worker groups in **different namespaces** (e.g., for resource quotas or network policy isolation), deploy separate Helm releases that share the same backend infrastructure. The primary release deploys backend, frontend, and the default worker pool. ```yaml theme={null} # values-primary.yaml backend: enabled: true frontend: enabled: true worker: enabled: true pools: default: enabled: true workerSubscribe: "all_but_kb" autoscaling: enabled: true type: "keda" minReplicas: 1 maxReplicas: 10 ``` ```bash theme={null} helm upgrade --install noxus ./cdk/helm/noxus-platform \ --namespace spotflow --create-namespace \ -f values.yaml -f values-primary.yaml ``` For each additional namespace, disable all non-worker components and provide the same database/Redis credentials. ```yaml theme={null} # values-kb-workers.yaml backend: enabled: false frontend: enabled: false relay: enabled: false # Same credentials as the primary release env: DATABASE: "spot" REDIS_PORT: "6379" secrets: DATABASE_URL: "postgresql://user:pass@db-host:5432/spot" REDIS_URL: "redis-host" REDIS_PASSWORD: "redis-pass" worker: enabled: true pools: kb: enabled: true workerSubscribe: "kb" resources: requests: cpu: "4" memory: "16Gi" limits: cpu: "4" memory: "16Gi" autoscaling: enabled: true type: "hpa" minReplicas: 1 maxReplicas: 5 targetCPUUtilizationPercentage: 75 nodeSelector: workload-type: kb ``` ```bash theme={null} helm upgrade --install noxus-kb ./cdk/helm/noxus-platform \ --namespace spotflow-kb --create-namespace \ -f values.yaml -f values-kb-workers.yaml ``` All worker releases **must** connect to the same PostgreSQL and Redis instances. Redis coordinates task distribution — workers in any namespace pick up tasks from their subscribed queues regardless of where they run. ### Cross-Namespace Considerations * **Secrets**: Each namespace gets its own K8s Secret. Use External Secrets Operator or a shared values file to keep credentials in sync. * **Service Account**: Worker-only releases still need a ServiceAccount with IRSA annotations for S3 access. * **KEDA**: ScaledObjects are namespace-scoped. Each release creates its own KEDA resources; the cluster-wide KEDA operator discovers them automatically. * **Network Policies**: Ensure worker namespaces can reach PostgreSQL, Redis, external APIs, and storage endpoints. *** ## Autoscaling Best for scaling based on actual queue depth. Supports **scale-to-zero**. KEDA polls PostgreSQL to count queued/running tasks and adjusts replicas to maintain a target ratio. ```yaml theme={null} worker: keda: postgresConnectionString: "postgresql://user:pass@host:5432/spot" pollingInterval: 15 query: >- SELECT COALESCE(COUNT(*), 0) FROM runs WHERE status IN ('Queued', 'Running') AND created_at > NOW() - '1 hour'::interval; targetQueryValue: "10" cronTrigger: enabled: true timezone: "UTC" start: "0 9 * * *" end: "0 20 * * *" desiredReplicas: 1 pools: default: autoscaling: enabled: true type: "keda" minReplicas: 1 maxReplicas: 10 behavior: scaleDown: stabilizationWindowSeconds: 300 policies: - type: Percent value: 10 periodSeconds: 60 ``` Individual pools can override the global KEDA query and target: ```yaml theme={null} autoscaling: type: "keda" keda: query: "SELECT COUNT(*) FROM runs WHERE status = 'Queued' AND queue_type = 'chat';" targetQueryValue: "5" pollingInterval: 10 ``` Best for simple CPU/memory-based scaling. **Cannot** scale to zero. ```yaml theme={null} autoscaling: enabled: true type: "hpa" minReplicas: 1 maxReplicas: 5 targetCPUUtilizationPercentage: 75 targetMemoryUtilizationPercentage: 85 ``` *** ## Run Archiving After each workflow run completes, the worker archives run data (state, progress, node IO, logs, content streams) from Redis to object storage (S3/GCS/Azure Blob). This ensures long-term persistence but adds per-run overhead from token acquisition and upload latency. ### Archive Mode (`ARCHIVE_MODE`) | Mode | Behavior | Use case | | ---------- | ---------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- | | `sync` | Archives immediately after each run completes (default) | Production — guarantees data is persisted before the worker moves on | | `async` | Skips per-run archiving; a periodic Celery task sweeps Redis and batch-archives completed runs | High-throughput deployments where per-run archiving creates bottlenecks | | `disabled` | No archiving — data stays in Redis until TTL expires or the orphan cleanup task runs | Benchmarking and stress testing | ### Configuration | Variable | Default | Description | | ------------------------ | ------- | ---------------------------------------------------------------- | | `ARCHIVE_MODE` | `sync` | Archive strategy: `sync`, `async`, or `disabled` | | `ARCHIVE_BATCH_INTERVAL` | `60` | Seconds between batch archive sweeps (only used in `async` mode) | ### Async Mode In `async` mode, a periodic Celery task (`batch_archive_runs`) runs every `ARCHIVE_BATCH_INTERVAL` seconds and archives all completed runs that have been idle in Redis for at least that duration. This eliminates per-run cloud storage token acquisition and reduces GCP/AWS auth pressure under high concurrency. ```yaml theme={null} # Helm values example env: ARCHIVE_MODE: "async" ARCHIVE_BATCH_INTERVAL: "30" ``` ### Disabled Mode Use `disabled` for pure performance benchmarking. Run data remains in Redis (subject to TTL) and can still be retrieved by the backend. The existing `archive_orphaned_data` periodic task (runs every 30 minutes) acts as a safety net and will eventually archive idle data regardless of mode. ```yaml theme={null} # Helm values for stress testing env: ARCHIVE_MODE: "disabled" ``` In `disabled` mode, run data that exceeds its Redis TTL (4 hours for node IO, 60 days for state/progress) will be lost if not archived by the orphan cleanup task. Do not use this mode in production. *** ## Verification After deploying, verify the setup: ```bash theme={null} # Worker deployments across namespaces kubectl get deployments -l app=worker --all-namespaces # Pool labels on pods kubectl get pods -l app=worker --all-namespaces --show-labels # KEDA ScaledObjects kubectl get scaledobjects --all-namespaces # HPAs kubectl get hpa --all-namespaces -l app=worker # PodDisruptionBudgets kubectl get pdb --all-namespaces -l app=worker # Verify queue subscription in worker logs kubectl logs -l app=worker --tail=50 | grep "WORKER_SUBSCRIBE" ``` Platform environment variables and configuration layers Secret management, per-pool injection, and rotation General scaling strategies and capacity planning Kubernetes deployment guide # Kubernetes Source: https://docs.noxus.ai/deployment/kubernetes/overview Deploy Noxus on Kubernetes or OpenShift using Helm and Terraform Kubernetes is the recommended production model for organizations requiring robust autoscaling, zone resilience, and platform-level operations. It is the only deployment model that offers per-workload worker isolation, queue-driven autoscaling, and geo-replication. ## Infrastructure as Code Deployment assets are maintained in the [noxus-infra](https://github.com/noxus-ai/noxus-infra) repository, which ships composed stacks for all three managed Kubernetes services. | Stack | Provisions | | :--------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- | | [`terraform/stacks/kubernetes/aws-eks`](https://github.com/noxus-ai/noxus-infra/tree/main/terraform/stacks/kubernetes/aws-eks) | VPC, EKS, RDS PostgreSQL, ElastiCache, S3, ACM, AWS Load Balancer Controller, external-dns | | [`terraform/stacks/kubernetes/gcp-gke`](https://github.com/noxus-ai/noxus-infra/tree/main/terraform/stacks/kubernetes/gcp-gke) | VPC with Private Service Access, GKE, Cloud SQL, Memorystore, GCS, managed certificate | | [`terraform/stacks/kubernetes/azure-aks`](https://github.com/noxus-ai/noxus-infra/tree/main/terraform/stacks/kubernetes/azure-aks) | VNet, AKS, PostgreSQL Flexible Server, Azure Cache, Blob Storage, ingress-nginx, cert-manager | Each stack is built from reusable per-cloud modules (`terraform/modules/`), so you can adopt individual pieces if you already run your own networking or data services. ### Helm Chart The platform itself is one Helm chart, shared by all three stacks: * **Granular Component Control**: Independent configuration for Backend, Frontend, Workers, Relays, and the Agent Sandbox. * **Advanced Scaling**: HPA and KEDA-driven worker scaling. * **Geo-replication**: Additional per-zone Deployments behind a single Service. * **Connection pooling**: An optional bundled PgBouncer. *** ## Helm Components | Helm section | Noxus component | Default | Description | | :------------- | :-------------- | :------ | :------------------------------------------------------------------------------------------------ | | `backend` | Noxus Backend | on | Core API and orchestration. Also serves the MCP endpoint at `/mcp`. | | `frontend` | Noxus Frontend | on | Web-based user interface. | | `worker.pools` | Noxus Workers | on | Scalable execution engines for flows and agents. | | `relay` | Noxus Relays | **off** | Webhook and event receiver endpoints. | | `sandbox` | Agent Sandbox | **off** | Isolated Run Code execution. **Required for plugins** — see [Agent Sandbox](/deployment/sandbox). | | `pgbouncer` | PgBouncer | **off** | Connection pooler. Recommended above a few worker replicas. | | `ingress` | Ingress | on | Public routing and TLS termination. | ### OpenShift The chart renders a standard Kubernetes `Ingress`; it does not currently template an OpenShift `Route`. To run on OpenShift, either enable the cluster's ingress-to-route controller so the `Ingress` is translated automatically, or create the `Route` objects alongside the release. Note that OpenShift's default SCCs also restrict the added capabilities the Agent Sandbox's `syd` jail requires. *** ## Kubernetes Runtime Diagram ```mermaid theme={null} flowchart TB IN[Ingress / Route] --> FE[Noxus Frontend Deployment] IN --> BE[Noxus Backend Deployment] IN --> RE[Noxus Relay Deployment] BE --> WP[Noxus Worker Pools] WP --> PG[(PostgreSQL)] WP --> RD[(Redis)] WP --> CS[(Cold Storage)] BE --> PG BE --> RD BE --> CS RE --> RD KEDA[KEDA ScaledObjects] --> WP HPA[HPA] --> BE HPA --> FE ``` *** ## Autoscaling Model ### Backend and Frontend Standard Horizontal Pod Autoscaler (HPA) manages replicas based on CPU and memory utilization. ### Workers Worker pools support two advanced scaling modes: * **Resource-based**: Standard HPA for CPU/Memory. * **Queue-driven**: KEDA-powered scaling based on task queue depth, allowing for scale-to-zero and rapid bursts. *** ## Advanced Worker Pool Management The Kubernetes deployment model offers sophisticated control over how AI workloads are isolated and scaled. ### Workspace Mapping Worker pools can be scoped to specific workspaces so a team's workloads run on dedicated compute: * `workerSubscribeWorkspaces` pins a pool to a comma-separated list of workspace IDs. * `workerUnsubscribeWorkspaces` **excludes** workspaces from a pool, so a catch-all pool can run everything else while leaving those workspaces to dedicated pools. Used together, these give a noisy tenant its own capacity without starving the rest. All pools in a release share the release's namespace. Placing pools in separate namespaces — for distinct network policies or resource quotas — means installing a separate release per namespace, each with its own worker pool configuration and its own copy of the platform secrets. ### Workload-Specific Scaling Not all AI tasks are created equal. You can configure independent scaling policies for different pools: * **Real-time Pools**: Optimized for low-latency agent responses with higher minimum replica counts. * **Batch Pools**: Configured with KEDA to scale-to-zero when idle and burst rapidly for high-volume data processing. * **GPU Pools**: Targeted scaling for AI-intensive operations like model inference or embedding generation. ### Secret Isolation Security can be hardened at the pool level by injecting secrets directly into specific worker deployments. This ensures that sensitive credentials (like proprietary API keys or database strings) are only accessible to the workers that actually require them, providing robust secret isolation across your organization. *** ## Typical Deployment Steps Using the composed stacks, Terraform provisions the infrastructure **and** installs the chart in one apply. ```bash theme={null} cd noxus-infra/terraform/stacks/kubernetes/aws-eks cp terraform.tfvars.example terraform.tfvars ``` Set your domain, admin email, platform version, and Auth0 application. Worker pools and resource sizing are passed through as raw chart values via `extra_values`, so you get the chart's full surface without the stack having to model every key. ```bash theme={null} tofu init -backend-config="bucket=my-tfstate" -backend-config="key=noxus/prod.tfstate" tofu apply ``` The stack creates the cluster, data services, ingress controller, pod identity, and the Helm release. Expect 25–35 minutes on a first apply. Terraform can create the database instance but not the extensions inside it. Run this once, from inside the network: ```bash theme={null} scripts/bootstrap-postgres.sh --host --user noxus ``` It creates the platform's five databases and the `vector`, `uuid-ossp`, and `pg_trgm` extensions. ```bash theme={null} kubectl get pods -n noxus ``` Confirm the Backend reached the database and that your worker pools cover every queue type you need — a single pool on the default `all_but_kb` has **no consumer for knowledge-base ingestion**. Terraform installs the Helm release over the Kubernetes API. If you make the API endpoint private, `tofu apply` must run from inside the network — a bastion, a self-hosted runner in the VPC, or over a VPN. Helm chart, Terraform modules, and the operational docs set. Instance classes, replica counts, and worker pool shapes at three scales. HPA, KEDA, and worker pool tuning. Required for plugins — off by default. # Endpoint & Port Reference Source: https://docs.noxus.ai/deployment/networking/endpoint-reference Concrete hosts and ports to allowlist for a self-hosted Noxus deployment Use this as a checklist when writing firewall/egress rules. Everything here is **outbound** unless stated otherwise. You only need the rows for features you actually use — enable incrementally. Hostnames reflect the platform's defaults and may evolve as providers change. Treat per-provider model and integration hosts as "enable the ones you use", not "allow them all". ## Inbound (edge) | Port | Service | Source | Required | | ---------------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------- | ------------------------------------------- | | `443` (`80` redirect) | Reverse proxy → frontend, `api.`, `relay.` hosts | Users' browsers / API clients | Yes (can be private/VPN) | | `443` on `relay.` host | Relay service | External SaaS webhooks (WhatsApp, Teams, Telegram, generic, Google Chat webhook mode) | Only for push channels — must be **public** | All other service ports (workers `8080`, plugin server `8500`, sandbox) are **internal only** and must not be exposed. ## Outbound — model providers | Provider | Host(s) | Notes | | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | OpenAI | `api.openai.com` | | | Anthropic | `api.anthropic.com` | | | Google Gemini (API) | `generativelanguage.googleapis.com` | | | Google Vertex AI | `*-aiplatform.googleapis.com`, `aiplatform.googleapis.com`, `aiplatform.*.rep.googleapis.com`, `cloudresourcemanager.googleapis.com`, `oauth2.googleapis.com` | Regional, global and multi-region (EU/US) inference hosts + GCP auth/IAM. Multi-region hosts don't support Private Google Access — use Private Service Connect on private networking | | Google Vertex reranker | `discoveryengine.googleapis.com`, `eu-discoveryengine.googleapis.com`, `us-discoveryengine.googleapis.com` | KB reranking uses Discovery Engine, NOT the aiplatform hosts. Which host is called follows the provider's region selection (multi-region EU/US, else global). If blocked, reranking silently degrades to un-reranked results — watch `KB_RERANK_TOTAL{result="fallback"}` | | Azure OpenAI | `.openai.azure.com` | Your Azure resource host (configurable) | | AWS Bedrock | `bedrock-runtime..amazonaws.com`, `sts.amazonaws.com` | Regional; plus STS for auth | | Mistral | `api.mistral.ai` | | | Groq | `api.groq.com` | | | Perplexity | `api.perplexity.ai` | | | DeepSeek | `api.deepseek.com` | | | Grok (xAI) | `api.x.ai` | | | OpenRouter | `openrouter.ai` | | Embeddings use the same provider hosts (e.g. OpenAI / Vertex embedding endpoints) — allowlisting a provider covers its embedding models. The one exception is the Vertex reranker, which calls Discovery Engine (see its row). For an air-gapped setup, point a provider's `base_url` at an in-network OpenAI-compatible server and skip these entirely. ## Outbound — integrations & OAuth Enable only the providers you connect. Each typically needs an **authorize** host (browser), a **token** host (backend, outbound), and an **API** host (nodes/tools, outbound). | Provider | Hosts | | ---------------------------------------------- | ---------------------------------------------------------------------------------------- | | Google (Drive, Gmail, Sheets, Docs, Calendar) | `accounts.google.com`, `oauth2.googleapis.com`, `*.googleapis.com`, `www.googleapis.com` | | Microsoft (Teams, SharePoint, OneDrive, Excel) | `login.microsoftonline.com`, `graph.microsoft.com` | | Slack | `slack.com`, `api.slack.com` | | Notion | `api.notion.com` | | GitHub | `github.com`, `api.github.com` | | Airtable | `airtable.com` | | Linear | `linear.app`, `api.linear.app` | | Calendly | `auth.calendly.com`, `api.calendly.com` | | Typeform | `api.typeform.com` | By default managed-provider OAuth is brokered by NCS (below). If you use NCS brokering you still need egress to each provider's **API** host to run its nodes, but the OAuth **token** exchange goes via NCS. With direct OAuth apps, you also need the **token** host above. ## Outbound — platform services & storage | Purpose | Host | Required | Notes | | ------------------------------------------------------------------------ | ------------------------------------------------ | ------------ | ---------------------------------------------------------------------------------- | | Noxus Control Service (OAuth broker, web tools, on-prem checkin/upgrade) | `ncs.app.noxus.ai` | Optional | Outbound-only. Drop for air-gap (lose auto-upgrade + NCS-brokered OAuth/web tools) | | Object storage (GCS) | `storage.googleapis.com` | One of these | Or… | | Object storage (S3) | `s3..amazonaws.com` (or your MinIO host) | One of these | …an in-network S3-compatible store | | Managed Noxus backend | `backend.noxus.ai` | SaaS only | Not used by self-hosted deployments | ## Outbound — web tools | Tool | Host | Notes | | --------------- | --------------------- | ------------------------------- | | Web search | `google.serper.dev` | Often relayed via NCS | | Web scrape | `app.scrapingbee.com` | Often relayed via NCS | | Logo enrichment | `logo.clearbit.com` | Used by some enrichment helpers | ## Outbound — telemetry (optional) All optional; leave unset to send nothing. | Purpose | Host | | ---------------------------- | ----------------------------------------------------- | | Error tracking (Sentry) | `*.ingest.de.sentry.io` | | Product analytics (Mixpanel) | `api.mixpanel.com` | | OpenTelemetry export | Your configured collector (set to an in-network host) | ## Outbound — build / plugin install (as needed) Only relevant if you build images in-network or install plugins at runtime. | Purpose | Host | | ---------------- | ------------------------------------------------ | | Python packages | `pypi.org`, `files.pythonhosted.org` | | Node packages | `registry.npmjs.org` | | Container images | your registry (`gcr.io`, `ghcr.io`, Docker Hub…) | ## Outbound — your own callbacks | Purpose | Host | | ----------------------- | ------------------------------------------ | | Run completion webhooks | Whatever `callback_url` you pass on a run | | Channel reply delivery | The channel provider's API (covered above) | ## Minimal-egress checklist For a tightly restricted deployment, the smallest viable egress set is usually: * [ ] **One** model endpoint (in-network OpenAI-compatible, or a single public provider) * [ ] Object storage (in-network S3-compatible, or one cloud storage host) * [ ] The specific integration API hosts you actually enable * [ ] (Optional) an in-network OTel collector * [ ] Nothing else — NCS, telemetry, web tools, and unused providers can stay blocked See [Outbound → Running with minimal egress](/deployment/networking/outbound#running-with-minimal-egress) for the step-by-step. # Inbound Connectivity Source: https://docs.noxus.ai/deployment/networking/inbound What must be able to reach your deployment — users, channel webhooks, and OAuth callbacks Inbound traffic falls into two groups with very different exposure needs: 1. **Your users** — browsers and API clients. Can stay entirely on a private network/VPN. 2. **External SaaS webhooks** — third-party services pushing events to you. Require a publicly reachable endpoint. ## Services and ports Self-hosted Noxus puts a reverse proxy (nginx on VM, an ingress controller on Kubernetes) in front of three HTTP services. Only the proxy needs to be exposed. | Service | Internal port | Public hostname (typical) | Who connects | | ----------------------- | -------------------------- | ------------------------- | -------------------------- | | Frontend (Next.js) | `8080` (`3000` on VM) | `app.example.com` | Users' browsers | | Backend API (FastAPI) | `8080` (`8100` on VM) | `api.example.com` | Browsers, API clients, SSE | | Relays (FastAPI) | `8080` (`5003` on VM) | `relay.example.com` | External SaaS webhooks | | Workers | `8080` (internal) | — | Never exposed | | Plugin server / sandbox | `8500` / `8080` (internal) | — | Never exposed | On Kubernetes these are three ingress hosts (main, `api.`, `relay.`) all routing to internal port `8080`. On the VM, nginx terminates TLS on `80`/`443` and proxies to the per-service ports. Only `443` (and `80` for redirect) needs to be open at the edge. ## Inbound from your users (UI, API, streaming) These power the product itself and are **always required**, but they only need to be reachable by your users — a LAN or VPN address is fine. * **Frontend + Backend API** over HTTPS. * **Server-Sent Events (SSE)** — long-lived `GET`/`POST` responses with `text/event-stream`, used for live run progress (`/v1/runs/{run_id}/events`) and agent replies (`/v1/conversations/{conversation_id}/stream` and `/events`). Make sure your proxy does **not buffer** these responses and allows long-lived connections (disable response buffering; set a generous read timeout — streams can run for minutes). * **WebSockets** — used by optional interactive features (sandbox shell, playbook recording). If your proxy needs explicit WebSocket upgrade rules, add them; these features simply won't work without them, but core usage is unaffected. A common self-hosting bug is an idle-timeout or buffering proxy that cuts SSE streams. If runs "hang" in the UI but complete server-side, check your proxy's buffering and timeout settings on the API host first. ## Inbound from external SaaS (channel webhooks) Agent and trigger **channels** differ in whether the external service *pushes* events to you (needs public inbound) or whether Noxus *pulls* them (outbound only). This is the single biggest restricted-networking consideration. | Channel | Mechanism | Needs public inbound? | Notes | | --------------------------- | ----------------------------------------------------------- | ------------------------ | ------------------------------------------------------------------------- | | **WhatsApp** (Cloud API) | Meta POSTs to your webhook | **Yes** | No polling alternative — webhook is mandatory | | **Microsoft Teams** | Teams POSTs change notifications | **Yes** | No polling fallback | | **Telegram** | `setWebhook` registers your URL; Telegram POSTs updates | **Yes** | A `getUpdates` polling fallback is **not** implemented | | **Generic webhook trigger** | External system POSTs to `/webhook/{group_id}/{trigger_id}` | **Yes** | The whole point is to receive external calls | | **Google Chat** | Webhook **or** Pub/Sub | **Only in webhook mode** | Switch to **Pub/Sub mode** to make it outbound-only | | **Slack** | **Socket Mode** (outbound WS) or Events webhook | **No, with Socket Mode** | Socket Mode opens an outbound WebSocket to Slack, so no inbound is needed | | **Gmail** | Relay polls the Gmail API | **No** | Outbound polling loop | | **Outlook / email** | Relay polls the provider | **No** | Outbound polling loop | | **Knowledge base sync** | Worker polls the source | **No** | Periodic outbound polling | In a private/VPN-only deployment you can still use **Slack (Socket Mode)**, **Gmail/Outlook**, **Google Chat in Pub/Sub mode**, and **KB sync** — they never need an inbound path. The push channels (WhatsApp, Teams, Telegram, generic webhooks) require exposing the **relay host** to the internet; expose only `relay.example.com` and keep the app itself private if you want to minimise surface area. ### Securing the relay endpoint If you must expose the relay for push channels, restrict it: each provider signs or carries a secret on its webhook (Slack signing secret, Telegram secret token, WhatsApp/Meta app secret, per-trigger tokens for generic webhooks). Keep the relay host on its own subdomain so you can apply WAF/rate-limit rules independently of the app. ## OAuth callbacks When a user connects an integration (Google, Microsoft, Slack, GitHub…), the provider sends the user's **browser** back to: ``` {BACKEND_URL}/integrations/oauth/callback ``` This is a **302 redirect in the user's browser**, *not* a server-to-server call from the provider. So the callback URL only needs to be reachable by your **users' browsers** — the same audience as the rest of the app. The subsequent token exchange (backend → the provider's token endpoint) is **outbound** (see [Outbound](/deployment/networking/outbound#integrations-oauth)). Implications: * If your users reach the app over a VPN, an internal `BACKEND_URL` works for OAuth — no public inbound required. * `BACKEND_URL` must exactly match the redirect URI registered in each OAuth app, and the frontend `redirect_uri` must share the configured `FRONTEND_URL` origin (the backend validates this). * If you can't expose an OAuth callback at all, fall back to **manual credential entry** (static API tokens) for providers that support it. # Outbound Connectivity Source: https://docs.noxus.ai/deployment/networking/outbound What Noxus connects out to — models, integrations, storage, control plane — and how to run with minimal egress Most of Noxus's "intelligence" lives behind outbound calls. In a locked-down network, egress rules are what determine which features work. This page explains the categories and the impact of restricting each; the [Endpoint reference](/deployment/networking/endpoint-reference) lists the concrete hosts and ports to allowlist. ## Categories of egress ```mermaid theme={null} flowchart LR WRK[Workers / Backend] --> M[Model providers] WRK --> I[Integration & OAuth APIs] WRK --> S[Object storage] WRK --> NCS[Noxus Control Service] WRK --> T[Telemetry - optional] WRK --> WT[Web search / scrape tools] WRK --> CB[Your callback / webhook URLs] ``` ### Model providers Every LLM, embedding, and reranker call is outbound to the provider's API. If egress to a provider is blocked, **models from that provider fail** and any node/agent relying on them errors out (fallback models from a reachable provider still work). You must be able to reach **at least one** model provider, or self-host one (see [minimal egress](#running-with-minimal-egress)). Cloud provider models (Vertex AI, Azure OpenAI, AWS Bedrock) also require egress to that cloud's auth/metadata endpoints, not just the inference endpoint. ### Integrations & OAuth Two distinct outbound flows: * **Token exchange & refresh** — after a user authorizes an integration, the backend calls the provider's **token endpoint** (outbound) to exchange the code and later refresh it. * **API calls** — running an integration node calls the provider's API (e.g. Google Drive, Microsoft Graph, Slack, Notion). Blocked egress to a provider disables that integration's nodes and any agent tools that use it. By default, OAuth for managed providers is **brokered by the Noxus Control Service** (NCS) — the secrets stay with NCS and the platform claims short-lived tokens. If you block NCS egress, use **direct OAuth apps** (your own client id/secret per provider) or static credentials instead. ### Object storage Files, run IO, and knowledge-base content live in object storage (Google Cloud Storage or S3-compatible). This egress is **required** unless you run an in-network S3-compatible store (e.g. MinIO) and point storage at it. Blocking it breaks uploads, file nodes, and KB ingestion. ### Noxus Control Service (NCS) NCS is Noxus's optional control plane. Self-hosted deployments use it for: * **OAuth brokering** for managed integration providers. * **Search / scrape relay** for the built-in web tools. * **On-prem checkin & auto-upgrade** — the worker runs an **outbound** checkin (no inbound needed) that lets Noxus push compose/image updates. NCS is **outbound-only** and **optional**. Without egress to it: * Auto-upgrade is disabled (upgrade manually instead). * NCS-brokered OAuth and the relayed search/scrape tools are unavailable — switch to direct OAuth apps and provider-native search keys. * **The platform otherwise runs normally.** ### Telemetry (optional — disable for air-gap) Error tracking (Sentry), product analytics (Mixpanel), and OpenTelemetry export are all outbound and **optional**. Leave their config unset and no telemetry egress occurs. For OTel, point the exporter at an **in-network** collector. ### Web tools The web-search and web-scrape tools call external services (a search API and a scraping API), typically via the NCS relay. Blocked egress disables those agent tools and any flow nodes that use them. ### Outbound webhooks (run callbacks & relays) When you pass a `callback_url` on a run, Noxus **POSTs the result outbound** to that URL on completion. This is the recommended pattern when you can't accept inbound — you receive results without exposing anything. The target must be in your **egress** allowlist (and able to receive the call). Channel reply delivery (Slack/Teams/etc. responses) is likewise outbound to the provider. ## Impact summary | Egress blocked | Impact | | --------------------------- | ------------------------------------------------------------------------------------ | | A specific model provider | That provider's models fail; reachable fallbacks still serve requests | | **All** model providers | No LLM/embedding calls — agents and AI nodes can't run. Self-host a model to recover | | A specific integration host | That integration's nodes/tools fail; others unaffected | | Object storage | File uploads, file nodes, and KB ingestion break | | NCS | No auto-upgrade, no NCS-brokered OAuth/web tools; core platform fine | | Telemetry hosts | None functional — telemetry is best-effort and optional | | Your callback host | Run callback webhooks are not delivered (run still completes) | ## Running with minimal egress To operate in a tightly restricted or air-gapped network: Use an OpenAI-compatible server inside your network (e.g. vLLM, Ollama, or a private Azure OpenAI). Configure the provider `base_url` to that host so no public model egress is needed. Register your own OAuth applications per provider (your client id/secret) so token exchange goes straight to the provider, bypassing NCS. For providers that support static API tokens, enter those directly. Point storage at an S3-compatible endpoint (e.g. MinIO) reachable inside your network. Leave the Sentry DSN, Mixpanel token, and OTel exporter endpoint unset (or set OTel to an internal collector). Skip NCS egress entirely. Upgrade the deployment by replacing image tags / compose files yourself. With the above, your required egress shrinks to your in-network endpoints plus whatever specific external integrations you deliberately keep. Verify against the [Endpoint reference](/deployment/networking/endpoint-reference). Web search/scrape, hosted-model providers, and managed-OAuth providers are the features most affected by an air-gap. Plan around in-network models and direct credentials, and the rest of the platform — flows, agents over your own models, knowledge bases, the editor — runs without public egress. # Networking Overview Source: https://docs.noxus.ai/deployment/networking/overview How a self-hosted Noxus deployment talks to the network — and what restricted connectivity breaks This section is for operators running **self-hosted Noxus** (VM docker-compose or Kubernetes) who need to define firewall, ingress, and egress rules. On the **managed cloud** (`app.noxus.ai`) all of this is handled for you — you only need your users to reach the app over HTTPS. Noxus has two independent connectivity surfaces. Treat them separately when you plan firewall rules: Traffic arriving at your deployment — your users' browsers and API clients, plus webhooks pushed by external SaaS (Slack, WhatsApp, Teams…). Traffic Noxus initiates — model providers, integration APIs, object storage, and the optional Noxus Control Service. ## The mental model ```mermaid theme={null} flowchart LR subgraph internet[Public internet] USR[Users' browsers / API clients] SAAS[External SaaS
Slack, Meta, Teams, Google…] LLM[Model providers] NCS[Noxus Control Service] end subgraph dep[Your deployment] FE[Frontend] BE[Backend API] RLY[Relays] WRK[Workers] end USR -->|HTTPS / SSE / WS| FE USR -->|HTTPS / SSE| BE SAAS -->|inbound webhooks| RLY BE -->|model + integration calls| LLM WRK -->|polling, callbacks, model calls| LLM WRK <-->|outbound checkin| NCS ``` ## Two kinds of inbound — don't conflate them | Inbound source | What it reaches | Can it be private (LAN/VPN)? | | -------------------------------------------------------------------------------------- | ------------------ | ----------------------------------------------------------------------------------------------------- | | **Your users** (browser, API clients, SSE streams) | Frontend + Backend | **Yes** — if all users are on your network/VPN, the app never needs a public address | | **External SaaS webhooks** (Slack events, WhatsApp, Teams, Telegram, generic webhooks) | Relays | **No** — these are server-to-server calls from the internet and require a publicly reachable endpoint | OAuth sign-in callbacks are **browser redirects**, not server-to-server calls. They only need to be reachable by your users' browsers — not by the OAuth provider's servers. See [Inbound connectivity](/deployment/networking/inbound#oauth-callbacks). ## What restricted networking costs you | Restriction | Still works | Breaks / degraded | | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | | **No public inbound** (private/VPN only) | The full app for internal users; polling channels (Gmail, Outlook, KB sync); Slack via Socket Mode; outbound run webhooks | Push channels: WhatsApp, Teams, Telegram, generic webhook triggers, Google Chat in webhook mode | | **No outbound** (air-gapped) | Core orchestration, the editor, data already in the system | Hosted model providers, hosted integrations, NCS-brokered OAuth & auto-upgrade, telemetry, web search/scrape tools | | **Outbound to allowlist only** | Everything you allowlist | Anything you forgot to allowlist — see the [Endpoint reference](/deployment/networking/endpoint-reference) | A fully **air-gapped** deployment is supported but constrained: you must point models at an in-network OpenAI-compatible endpoint, use direct (non-NCS) OAuth apps or static credentials, and disable telemetry. The [Outbound](/deployment/networking/outbound#running-with-minimal-egress) page walks through the minimal-egress setup. # Backup & Recovery Source: https://docs.noxus.ai/deployment/operations/backup-recovery Enterprise backup strategies, disaster recovery, and data resilience for Noxus Noxus implements a multi-layered backup and recovery strategy to ensure that your AI infrastructure is resilient to data loss, service failures, and regional outages. ## Backup Strategy Our backup philosophy is built on the principle of **continuous availability**. We categorize data into three distinct tiers with specific recovery objectives. **PostgreSQL** - **Method**: Daily snapshots + Point-in-Time Recovery (PITR). - **Scope**: User data, flow definitions, and knowledge base metadata. **S3 / GCS / MinIO** - **Method**: Versioning + Cross-region replication. - **Scope**: Knowledge base documents, run-level logs, and artifacts. **Secrets & Env** - **Method**: Infrastructure-as-Code (IaC) versioning + Secret Manager backups. - **Scope**: `auth_config`, API keys, and deployment parameters. *** ## Data Layer Resilience ### PostgreSQL (Persistence Layer) PostgreSQL is the source of truth for the platform. For production environments, we recommend: * **Automated Snapshots**: Daily full snapshots with a minimum 30-day retention. * **PITR**: Continuous transaction log (WAL) archiving to allow recovery to any specific second within the retention window. * **Multi-AZ Failover**: Deploy with a synchronous standby in a separate availability zone for zero-downtime failover. ### Object Storage (Liquid Data) As part of our **Liquid Data** architecture, object storage handles the bulk of your AI assets: * **Versioning**: Enable bucket versioning to protect against accidental deletions or overwrites. * **Lifecycle Policies**: Automatically transition older logs and artifacts to lower-cost storage classes (e.g., Glacier or Coldline) to optimize budgets. * **Replication**: For mission-critical deployments, enable cross-region replication to ensure data availability even during a total cloud region failure. *** ## Recovery Objectives (RTO/RPO) Noxus is designed to help you meet strict enterprise recovery targets: | Objective | Target | Description | | :----------------------- | :----------- | :------------------------------------------------------------------ | | **RPO (Recovery Point)** | \< 5 Minutes | The maximum amount of data loss you can tolerate (driven by PITR). | | **RTO (Recovery Time)** | \< 1 Hour | The maximum time allowed to restore the platform to full operation. | *** ## Disaster Recovery (DR) Patterns Depending on your deployment model, you can implement several DR patterns: Maintain a secondary deployment in a different region. Data is continuously replicated, and the secondary stack can be scaled up rapidly during a failover. Run Noxus services in multiple regions simultaneously. Traffic is routed to the nearest healthy region, providing the highest level of availability and lowest latency for global users. For isolated environments, backups are stored on encrypted, physically separate media and recovered using verified offline procedures. *** ## Operational Readiness **The Restore Drill**: A backup is only as good as its last successful restore. We recommend performing quarterly restoration drills in a non-production environment to validate your runbooks. Use the Terraform and Helm assets in `noxus-infra` to automate the provisioning of backup resources. Set up alerts for failed snapshots or replication lag in your monitoring dashboard. Maintain a clear, step-by-step recovery guide that includes DNS switching and secret restoration. **A pre-upgrade snapshot is your only rollback path for schema changes.** The platform runs Alembic migrations on Backend startup and does not reverse them. Rolling back a release that migrated means restoring the database, then deploying the previous version — so take an explicit snapshot before every version bump, not just a reliance on continuous backups. Understand the three storage layers being backed up. Migration ordering, worker draining, and the rollback procedure. # Logging Source: https://docs.noxus.ai/deployment/operations/logging Centralized logging, audit trails, and troubleshooting for Noxus Noxus implements a structured logging strategy to ensure that every event—from a user login to a complex flow execution—is recorded and searchable across your entire deployment. ## Logging Strategy The platform follows a **structured logging** approach, emitting logs in JSON format to facilitate easy ingestion and analysis by modern log management systems. * **Aggregation**: Logs are tagged by component (`noxus-backend`, `noxus-worker`, etc.) and workspace ID. * **Correlation**: Every request is assigned a unique **Correlation ID**, allowing you to trace a single action across the frontend, backend, and worker pools. * **Audit Trails**: Critical security and management actions are recorded in a dedicated audit log for compliance and forensic analysis. *** ## Collection Patterns Noxus supports standard log collection patterns for both cloud and on-premises environments. **Native Log Routing** * Containers emit logs to `stdout/stderr`. * Use a sidecar or daemonset (e.g., FluentBit, Promtail) to forward logs to a central backend. * Automatic labeling of logs with namespace, pod, and container metadata. **Docker & Systemd Logs** * Collect logs via the Docker logging driver or `journald`. * Forward to a centralized destination using a lightweight agent. *** ## What We Log To provide a complete picture of system activity, Noxus captures the following data points: | Category | Content | | :--------------------- | :--------------------------------------------------------------- | | **Flow Execution** | Node starts/stops, input/output schemas, and execution timing. | | **Agent Interactions** | Conversation metadata, tool calls, and model reasoning steps. | | **System Events** | Service lifecycle events, dependency health changes, and errors. | | **Security Audit** | Authentication attempts, role changes, and API key usage. | *** ## Run-Level Logging & Archiving In addition to centralized system logs, Noxus captures detailed execution logs for every individual **Agent** and **Flow Run**. * **Execution Context**: These logs provide a step-by-step record of node executions, tool calls, and model reasoning for a specific run. * **Cold Storage Archiving**: To ensure long-term availability and compliance, these run-level logs are autonomously archived in **Object Storage** (S3/GCS/MinIO) alongside the run's artifacts. * **Accessibility**: Archived logs remain associated with their respective Run ID, allowing for historical review and forensic analysis even after the active execution state has been purged from the primary database. **Security First**: Noxus is configured to automatically redact sensitive information. However, ensure your log management system also follows strict data privacy policies. Never log raw secrets or full credential payloads. *** ## Troubleshooting with Logs When investigating an issue, we recommend the following workflow: 1. **Identify**: Find the Correlation ID or Run ID associated with the error. 2. **Filter**: Search your log backend (Loki, Elasticsearch, Datadog) using the ID. 3. **Analyze**: Review the sequence of events across services to identify the point of failure. Combine logs with real-time metrics for a 360-degree view of your deployment. # Monitoring Source: https://docs.noxus.ai/deployment/operations/monitoring Enterprise observability, real-time metrics, and health tracking for Noxus Noxus provides deep visibility into its distributed architecture through standardized health endpoints, Prometheus-compatible metrics, and distributed tracing. ## Observability Architecture The platform is designed to be monitored at three distinct layers: the **Service Layer**, the **Coordination Layer**, and the **Data Layer**. ```mermaid theme={null} flowchart LR FE[Noxus Frontend] --> PM[Prometheus] BE[Noxus Backend] --> PM W[Noxus Workers] --> PM RE[Noxus Relays] --> PM BE --> OT[OpenTelemetry Collector] W --> OT RE --> OT PM --> GR[Grafana] ``` *** ## Key Performance Indicators (KPIs) To ensure a stable production environment, we recommend monitoring the following signals: * **Latency**: P95/P99 response times for API endpoints. - **Error Rates**: 4xx and 5xx response codes. - **Throughput**: Requests per second (RPS). * **Queue Depth**: Number of tasks waiting in the broker (Redis/RabbitMQ). - **Processing Lag**: Time between task creation and execution start. - **Worker Utilization**: CPU/Memory usage per worker pool. * **Connection Pressure**: Active vs. maximum allowed connections. - **Slow Queries**: Queries exceeding the 500ms threshold. - **IOPS**: Disk I/O utilization for vector search operations. * **Memory Saturation**: Percentage of available memory used. - **Eviction Rate**: Frequency of keys being removed due to memory limits. - **Command Latency**: Time taken to process coordination requests. *** ## Health & Metrics Endpoints All Noxus services expose standardized endpoints for automated health checks and metrics collection: * **Health Checks**: `/status/health` (Used by Kubernetes Liveness/Readiness probes). * **Prometheus Metrics**: `/metrics` (Exposes internal service counters and histograms). Noxus provides a set of **Default Grafana Dashboards** in the `noxus-infra` repository. These pre-configured dashboards provide immediate visibility into API performance, worker queue health, and resource utilization across your deployment. In Kubernetes deployments, the official Helm charts automatically annotate pods for Prometheus scraping, ensuring zero-config observability. *** ## Alerting Strategy We recommend setting up alerts for the following critical conditions: 1. **Service Availability**: Any core service reporting a non-healthy status. 2. **Queue Backlog**: Task queue depth exceeding defined thresholds for more than 5 minutes. 3. **Database Saturation**: PostgreSQL connection usage exceeding 80%. 4. **Model Provider Failures**: Sustained 5xx errors from external AI providers (OpenAI, Anthropic, etc.). Pair metrics with centralized logs for faster root cause analysis. Use monitoring signals to drive automated scaling policies. # Scaling Source: https://docs.noxus.ai/deployment/operations/scaling Dynamic scaling strategies for control-plane and execution-plane workloads Noxus is designed with a decoupled architecture that allows you to scale the **Control Plane** (API/Frontend) and the **Execution Plane** (Workers) independently based on their unique workload profiles. ## Service Scaling Model The platform utilizes different scaling strategies for its various components to optimize for both performance and cost. **Frontend & Backend** - Scaled via standard **HPA** (Horizontal Pod Autoscaler). - Triggers based on CPU and Memory utilization. - Optimized for consistent API responsiveness. **Worker Pools** - Scaled per-pool via **KEDA** or **HPA**. - Triggers based on task queue depth or resource usage. - Optimized for high-throughput AI processing. *** ## Advanced Worker Pool Scaling Worker pools are the most dynamic part of the Noxus infrastructure. They support sophisticated scaling patterns to handle unpredictable AI workloads. ### KEDA-Driven Scaling (Queue-Based) For most production environments, we recommend using **KEDA** (Kubernetes Event-driven Autoscaling) for worker pools: * **Scale-to-Zero**: Automatically shut down workers when no tasks are in the queue to save costs. * **Rapid Bursts**: Instantly spin up dozens of workers when a high-volume batch job is submitted. * **Queue Awareness**: Scaling is based on the actual number of pending tasks in Redis or RabbitMQ, not just CPU usage. ### Resource-Based Scaling (HPA) For workloads with consistent, long-running tasks, standard HPA can be used to maintain a steady pool of workers based on CPU or Memory saturation. *** ## Multi-Region & Multi-Zone Scaling For global enterprises, Noxus supports scaling across multiple geographic regions and availability zones. * **Regional Replicas**: Deploy independent Frontend and Backend replicas in different regions to minimize latency for global users. * **Zone Resilience**: Distribute worker pools across multiple availability zones to ensure continuous operation during a zone failure. * **Independent Policies**: Configure unique autoscaling rules for each region based on local traffic patterns. *** ## Scaling Best Practices * **Watch the database first**: PostgreSQL holds the job queue as well as the application data, so it becomes the bottleneck before compute does. The failure is abrupt — everything works until `max_connections` is reached, then every new pod fails to connect. Alert at 80%. * **Right-Size Pools**: Create dedicated worker pools for different task types (e.g., a GPU pool for inference, a high-memory pool for document processing). * **Size workers for memory, not CPU**: A worker holds run state, node outputs, and — during ingestion — whole documents plus embedding batches. An undersized worker is OOM-killed and its job retries, so the symptom is unexplained slow throughput rather than an obvious crash. * **Test Your Limits**: Conduct regular load tests to understand the scaling latency of your infrastructure (how long it takes to spin up a new worker). ### Signals worth alerting on | Signal | What it means | | :------------------------------ | :-------------------------------------------------------------------------------- | | Queue depth not draining | Workers undersized, or a queue type has no subscribed pool | | Time-to-start climbing | Rising with flat depth: workers busy. Rising with growing depth: too few workers. | | DB connections near the ceiling | Add a connection pooler before adding replicas | | Event-loop lag sustained | Something is blocking the async loop and degrading every request in that process | Learn how to configure scaling parameters in your Helm values. Understand how to scale your data layer alongside your compute. Instance classes and pool shapes at small, medium, and large scale. Symptoms and their usual causes. # Introduction Source: https://docs.noxus.ai/deployment/overview Deploying and operating Noxus in your own infrastructure Noxus is built with a decoupled, cloud-native architecture that can be deployed across a variety of environments—from single virtual machines to highly available Kubernetes clusters. ## Deployment Philosophy Our infrastructure is designed to give you complete control over your data and compute resources. All deployment scripts, configurations, and automation tools are maintained in our [noxus-infra](https://github.com/noxus-ai/noxus-infra) repository. Whether you're running in a public cloud, a private data center, or a strictly regulated air-gapped environment, Noxus provides the tools to ensure security, scalability, and operational excellence. Understand the core components, traffic flow, and runtime topology of the Noxus platform. Choose the right path for your needs: from simple VM setups to enterprise-grade Kubernetes. *** ## Choosing a Deployment Model | | **Virtual Machine** | **Serverless Containers** | **Kubernetes** | | :----------------------- | :------------------------------------- | :------------------------------------------------------------ | :------------------------------------ | | Scales to | One host | Moderate | Hundreds of workers, multi-zone | | Worker pool isolation | No | Coarse | Yes — per workload and per workspace | | Queue-driven autoscaling | No | No | Yes, via KEDA | | Availability guarantees | None | Managed | Multi-AZ | | Operational burden | Lowest | Low | Highest | | Good for | Trials, POCs, air-gapped single-tenant | Teams without Kubernetes experience; bursty low-baseline load | Production at scale, strict isolation | Noxus-infra's [choosing-a-deployment guide](https://github.com/noxus-ai/noxus-infra/blob/main/docs/choosing-a-deployment.md) covers the failure modes of each model in more detail. *** ## What You Need Before Starting Every deployment model requires the same four things: The platform serves three hostnames: ``, `api.`, and `relay.`. These must match the `FRONTEND_URL`, `BACKEND_URL`, and `RELAY_URL` variables — the Backend builds absolute URLs from them, so a mismatch produces redirects that dead-end rather than an obvious error. An Auth0 tenant with an application configured for your domain, or an OIDC provider. TLS is mandatory — Auth0 refuses non-HTTPS callbacks. To pull the platform container images. A single version selects every service image. Mixed versions are not supported — the Backend and Workers share a schema and job format. **PostgreSQL must be 15 or newer.** The platform's migrations create the `vector`, `uuid-ossp`, and `pg_trgm` extensions on first boot. On Azure Database for PostgreSQL these must additionally be present in the `azure.extensions` allowlist, or the Backend fails to start with a permission error. *** ## What Runs in Noxus The platform consists of several core services that work together to orchestrate and execute your AI workflows. ```mermaid theme={null} flowchart LR U[Users / API Clients] --> FE[Noxus Frontend] U --> BE[Noxus Backend] INT[External Integrations] --> RE[Noxus Relays] BE --> W[Noxus Workers] W --> SB[Agent Sandbox] W --> PG[(PostgreSQL + pgvector)] W --> RD[(Redis)] W --> CS[(Cold Storage: S3/GCS/MinIO)] BE --> PG BE --> RD RE --> PG RE --> RD ``` The Backend and Frontend are always required. **Relays** and the **Agent Sandbox** are optional — but the sandbox is required for plugins and for isolated Run Code execution, and it is off by default. See [Agent Sandbox](/deployment/sandbox). *** ## Core Infrastructure Pillars Manage environment variables, secrets, and connections to databases and storage. Implement robust authentication, SSO, and granular authorization scopes. Monitor system health, manage logs, and handle backup and recovery procedures. *** ## Getting Started If you're new to deploying Noxus, we recommend starting with our **Architecture** guide to understand how the system components interact, followed by the **Virtual Machine** deployment for your first proof-of-concept. Dive into the technical details of the Noxus platform components. # Agent Sandbox Source: https://docs.noxus.ai/deployment/sandbox Isolated execution for Run Code nodes and plugins, and what each deployment model can offer The Agent Sandbox is where Noxus runs code it did not write: **Run Code** nodes and **all plugin execution**. It is a separate service with its own isolation model, and it is **not deployed by default**. **Plugins require the sandbox.** The plugin system is switched on by the presence of `SANDBOX_MANAGER_URL`, which is only injected into the Noxus Backend and Workers when the sandbox is deployed. Without it, plugin nodes and triggers do not load — and nothing raises an error. They are simply absent from the platform. If you are upgrading from a release that ran a standalone plugin server, you must enable the sandbox or you will lose your plugins. ## When you need it You use plugins, or your workflows contain **Run Code** nodes that should be properly isolated. No plugins and no Run Code nodes. The rest of the platform is unaffected by leaving it off. *** ## Isolation modes The sandbox runs jailed processes. Which jail it can use depends on what your platform lets a container request. | Mode | Isolation | Requires | Use when | | :-------- | :----------------------------------------------- | :----------- | :------------------------------------------------------------------------ | | `syd` | Syscall filtering **and** network jail | `SYS_PTRACE` | Default. Any deployment running untrusted or user-authored code. | | `minimal` | `chroot` plus egress restricted by NetworkPolicy | `SYS_ADMIN` | Only when the platform refuses `SYS_PTRACE`, and only for code you trust. | `minimal` constrains the filesystem and the network but **not syscalls**. It is meaningfully weaker than `syd`. Treat it as a compatibility fallback, not an equivalent option. ### Platform compatibility | Platform | `syd` jail | Notes | | :-------------------------------- | :---------------- | :-------------------------------------------------------------------------------------- | | EKS | Supported | Works on Auto Mode and managed node groups. | | GKE Standard | Supported | | | **GKE Autopilot** | **Not supported** | Autopilot refuses `SYS_PTRACE`. You must use `minimal`, or move to Standard node pools. | | AKS | Supported | | | Restrictive PodSecurity admission | Depends | A policy that blocks added capabilities blocks `syd`. | The network jail in `minimal` mode is enforced by a Kubernetes NetworkPolicy, which requires a CNI that implements policy. The `azure-aks` stack in [noxus-infra](https://github.com/noxus-ai/noxus-infra) sets `network_policy = "cilium"` for this reason; the default CNIs on EKS and GKE already enforce policy. Without an enforcing CNI the NetworkPolicy object exists and does nothing. *** ## Configuration | Variable | Default | Description | | :----------------------- | :------ | :--------------------------------------------------------------------------------------- | | `SANDBOX_MANAGER_URL` | `""` | URL of the sandbox manager. **Empty disables the sandbox and the entire plugin system.** | | `USE_REMOTE_SANDBOX` | `true` | Route Run Code through the sandbox service rather than executing locally. | | `REMOTE_SANDBOX_BACKEND` | `""` | Remote execution backend. `syd` in normal use. | | `SANDBOX_BACKEND` | `syd` | Jail implementation. | | `LOCAL_SANDBOX_BACKEND` | `deno` | Worker-local fallback — see below. | | `PLUGIN_CALL_TIMEOUT` | `240.0` | Seconds before a plugin call is abandoned. | ### The local fallback matters `LOCAL_SANDBOX_BACKEND` is what a Worker uses when the remote sandbox is off or unhealthy: * **`none`** — no local execution. The safest setting when a remote sandbox is present, and the default the Helm chart selects when `sandbox.enabled=true`. * **`deno`** — a worker-local Deno/Pyodide runtime. Reasonable isolation, limited capability. * **`subprocess`** — runs user code as a plain subprocess **in the Worker's own process tree**. Much weaker isolation. Do not use this in a multi-tenant deployment. Setting `LOCAL_SANDBOX_BACKEND=subprocess` as a way to "make Run Code work without deploying the sandbox" gives untrusted code the Worker's filesystem, network, and environment — including its database credentials. *** ## Deployment The sandbox is a Deployment in the Noxus Helm chart, off by default: ```yaml theme={null} sandbox: enabled: true jail: syd # or "minimal" where SYS_PTRACE is unavailable localBackend: "" # empty selects the per-mode default ("none" when enabled) storage: "10Gi" shmSize: "512Mi" resources: requests: { cpu: "1", memory: "2Gi" } limits: { cpu: "2", memory: "4Gi" } ``` Enabling it also injects `SANDBOX_MANAGER_URL` into the Backend and Workers, and applies the sandbox NetworkPolicy. In the Terraform stacks the equivalent is `sandbox_enabled = true`, which is the default there. Deploy the sandbox as an additional internal container service, reachable only from the Backend and Workers — it should not have public ingress. Point `SANDBOX_MANAGER_URL` at its internal address. Syscall-level jailing depends on capabilities that managed container runtimes may not grant. Verify that your platform permits the sandbox's required capabilities before relying on `syd` isolation here. The appliance image runs the sandbox as a Compose service (`agentsandbox`) and sets `SANDBOX_MANAGER_URL=http://agentsandbox:8000` automatically. No additional configuration is needed. *** ## Filesystem and state The sandbox uses a copy-on-write filesystem whose state is periodically synced to object storage, so the pod itself can run on ephemeral disk. | Variable | Default | Description | | :------------------------- | :------------ | :------------------------------------------ | | `SANDBOX_FS_ENABLED` | `"true"` | Enable the synced copy-on-write filesystem. | | `SANDBOX_FS_PREFIX` | `"sandboxes"` | Key prefix within the storage bucket. | | `SANDBOX_FS_SYNC_INTERVAL` | `"300"` | Seconds between syncs. | It reuses the platform's existing storage configuration — `BUCKET_CLIENT`, `SPOTFLOW_STORAGE_BUCKET`, and whatever credentials or workload identity the Backend and Workers already use. No separate bucket is required. *** ## Sizing The sandbox's own resource requests cover the **manager**, not the code it runs. Jailed processes draw from the same container limits, so a deployment running heavy Run Code workloads needs headroom above the defaults. Start at 1 vCPU / 2 GiB requests with 2 vCPU / 4 GiB limits, and raise the limits if you see sandbox restarts under load. *** ## Health and troubleshooting The sandbox manager exposes `/health`, used for all three probes. | Symptom | Cause | | :--------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------- | | Plugins missing from the platform, no error anywhere | Sandbox not deployed, so `SANDBOX_MANAGER_URL` is empty | | Sandbox pods stuck `Pending` or failing admission | `SYS_PTRACE` refused — GKE Autopilot or a restrictive PodSecurity policy. Use `minimal`. | | Run Code executes but has no network access | Expected. The NetworkPolicy restricts egress by design; widen it deliberately rather than disabling the sandbox. | | Sandbox restarts during heavy Run Code use | Container limits exhausted by the jailed processes. Raise them. | Build plugins that run inside the sandbox. The full platform variable reference. # API Keys & System Keys Source: https://docs.noxus.ai/deployment/security/api-keys Workspace keys, tenant-scoped system keys, and the permission boundary between them Programmatic access to the platform uses **API keys**. There are two kinds, separated by a security boundary that is enforced in code: ordinary **workspace keys** and **system keys**. ## Workspace keys A workspace key is **scoped to a single workspace** and inherits a set of `resource:*` permissions (read, edit, delete, run, advanced, plus opt-in scopes like `sandboxes:run`). It's the key you use for the [SDK](/sdk/concepts/authentication), [REST API](/api-reference/introduction), and [MCP server](/sdk/mcp/overview). Create one in **Settings → Organization → Workspaces → (pick a workspace) → API Keys**. A workspace key can read and drive resources **within its workspace**. It can never perform tenant-wide administration — and, by design, it can't even hold the permissions that would allow it. ## System keys Some operations are **tenant-wide**, not workspace-scoped: creating or inviting users, creating/deleting workspaces, managing providers, billing, and org-level settings. These require the tenant-wide permissions: ``` users:read users:invite users:delete workspace:read workspace:create workspace:delete providers:manage billing:manage org:admin org:analytics ``` Those permissions may live **only** on a **system key** — an API key homed in the tenant's hidden `admin` **system workspace**. This is a hard invariant, enforced in two places: * **At mint time** — a request to put any tenant permission on an ordinary workspace key is rejected. * **At resolution time** — a tenant-scoped endpoint only accepts a key that actually resides in the admin system workspace, even if a permission somehow ended up on a workspace key. The net effect: a leaked workspace key can never escalate to tenant administration. The blast radius of a workspace key is exactly its workspace. ### Minting system keys Only a **tenant admin** can create system keys. They're managed through the admin surface: * **SDK** — `client.admin.create_system_key(name, permissions=[...])`. See [Admin](/sdk/resources/admin). * **REST** — `POST /v1/admin/system-keys`. See the [System keys API](/api-reference/v1--system-keys/list-system-keys). Grant a system key only the tenant permissions it needs (e.g. a provisioning bot that creates workspaces needs `workspace:create`, not `billing:manage`). ## Operating guidance Give each key the minimum permissions for its job. Prefer a workspace key unless the task is genuinely tenant-wide. Rotate keys periodically and delete unused ones. A key's value is shown once at creation — store it in a secret manager. Keys are secrets. Keep them server-side or in trusted environments; never embed them in browser or mobile apps. Key-driven actions are attributed to the key. See [Auditability](/deployment/security/auditability). See also [Authentication](/deployment/security/authentication) and [Authorization](/deployment/security/authorization) for how keys and permissions are verified across the platform. # Auditability Source: https://docs.noxus.ai/deployment/security/auditability Detailed overview of platform audit logs, API logging, and accountability Noxus provides a comprehensive auditability framework designed to meet the most stringent enterprise security and compliance requirements. Every significant action within the platform is recorded, attributed, and stored in a tamper-proof manner. ## Audit Log Architecture The audit system captures events at two primary levels: **Administrative/Management Actions** and **API Access**. ### 1. Platform Audit Logs Platform audit logs capture "who did what and when" regarding the management of resources. These logs are generated whenever a user or API key performs an action that modifies the state of the platform. #### What is Logged? Each audit log entry contains a high-fidelity record of the event: | Field | Description | | :----------------- | :-------------------------------------------------------------------------------------------------- | | **Timestamp** | The exact UTC time the action occurred. | | **Identity** | The User ID, Email, and/or API Key ID responsible for the action. | | **Action** | The specific operation performed (e.g., `create`, `update`, `delete`, `execute`, `login`). | | **Resource** | The type and unique ID of the resource (e.g., `workflow`, `agent`, `knowledge_base`, `user_group`). | | **Context** | The Tenant and Workspace identifiers where the action took place. | | **Payload** | A JSON representation of the request body and metadata associated with the change. | | **Route & Method** | The specific API route and HTTP method used. | #### Who is Logged? * **Platform Users**: Actions performed via the web interface. * **Service Accounts**: Actions performed by automated systems using API keys. * **System Administrators**: Global configuration changes and tenant-level management. *** ### 2. API Call Logs In addition to state-changing actions, Noxus maintains detailed logs of every incoming API request to ensure complete visibility into platform usage. #### Captured Data Points * **Performance**: Exact duration of the request in milliseconds and the resulting HTTP response code. * **Attribution**: Mapping of the call to a specific `tenant_id`, `user_id`, and `api_key_id`. * **Routing**: The specific resource endpoint accessed. * **Timing**: Precise start and end times for every call. *** ## Accountability & Traceability Noxus uses **Correlation IDs** to link related events across different services. This allows security teams to trace a single user action from the initial frontend request through the backend orchestration and down to the specific worker pool execution. ### Log Integrity * **Persistence**: Audit logs are stored in a dedicated, indexed database layer (`audit_logs` and `api_call_logs`) separate from transient application state. * **Redaction**: Sensitive information such as passwords or raw secret values are automatically redacted before being committed to the logs. * **Retention**: Organizations can define custom retention policies to meet legal and compliance obligations. *** ## SIEM & External Integration For centralized security monitoring, Noxus supports exporting audit and access logs to external **SIEM** (Security Information and Event Management) platforms. * **Supported Backends**: Elasticsearch, OpenSearch, Splunk, Datadog, and AWS CloudWatch. * **Format**: Logs are emitted in structured JSON format, making them ready for immediate ingestion and dashboarding. * **Alerting**: External systems can be configured to trigger alerts based on specific audit patterns, such as multiple failed login attempts or unauthorized resource access. See how auditability fits into our broader defense-in-depth strategy. Learn how to pair audit logs with real-time performance metrics. # Authentication Source: https://docs.noxus.ai/deployment/security/authentication Identity management and authentication configuration in Noxus Noxus provides a flexible and secure authentication system designed for enterprise environments. While **Auth0** is utilized as the default underlying identity provider, the platform's behavior is managed through a comprehensive `auth_config` system. ## Supported Connections Noxus supports multiple authentication methods, allowing you to mix and match providers based on your security requirements. Native support for **Google OAuth2**, **GitHub**, and **Microsoft** (Windows Live) logins. Robust **SAML** and **SAMLP** integration for seamless federation with corporate identity providers. *** ## Configuration Surface (`auth_config`) The platform's authentication behavior is governed by a central configuration schema that controls everything from branding to complex attribute mapping. ### Whitelabeling & UI Customize the login experience to match your corporate identity: * **Logo & Size**: Configure custom `whitelabel_logo` and adjust its display size. * **Branding**: Set a custom `whitelabel_name` and `whitelabel_primary_color`. ### Security & Tenant Policies * **Domain Restriction**: Use `allowed_domains` or `allowed_email_pattern` to restrict access to specific corporate domains. * **Email Verification**: Enforce `force_email_verification` for all new signups. * **MFA**: Globally require multi-factor authentication via `require_mfa`. * **Session Control**: Define `session_duration_hours` to manage login persistence. ### Workspace & Onboarding * **Autojoin**: Set `autojoin_tenant_id` to automatically place new users (from any signup path — password, OAuth, SAML/SSO) into a specific tenant rather than prompting them to create one. Required when SAML attribute mapping references tenant roles or workspaces. * **Personal Workspaces**: Toggle `allow_personal_workspace` to enable a per-user "personal workspace" option for invites and SAML attribute mappings. When on, the Add User dialog exposes a "Create a personal workspace" toggle, and "Personal Workspace" becomes a valid choice inside SAML `default_workspaces` / rule workspaces. * **Onboarding**: Use `skip_onboarding` for a more streamlined entry for experienced users. *** ## SAML Attribute Mapping For enterprise deployments using SAML, Noxus offers an advanced **Attribute Mapping** engine. This allows you to autonomously manage user permissions based on their identity provider groups or attributes. ### Mapping Rules You can define rules that match specific SAML attributes (e.g., `groups`, `department`, `role`) using modes like `exact`, `contains`, or `regex`. ### Automated Provisioning When a rule matches, Noxus can: * **Assign Workspaces**: Automatically add users to specific workspaces with predefined roles (Admin, Editor, Reader). * **Create Workspaces**: Dynamically create workspaces on-the-fly if they don't exist. * **Set Tenant Roles**: Assign the user's global role within the organization. * **Default Workspaces**: Set a specific workspace as the user's default landing environment. *** ## Redirect Management Noxus supports sophisticated redirect logic to handle complex multi-domain environments. You can define a `redirect_map` that routes users to different URLs based on their login domain, including support for wildcard patterns (e.g., `*.example.com`). Learn how to manage permissions after a user is authenticated. See how to securely store your authentication credentials. # Authorization Source: https://docs.noxus.ai/deployment/security/authorization Scope-based authorization model for workspaces and organization-level administration Noxus authorization is permission-driven and split between: * **Workspace-level permissions** — govern what a user can do inside a specific workspace. * **Organization-level permissions** — control cross-workspace and tenant-wide administration. *** ## Workspace-Level Permissions These permissions are scoped to a workspace and stored as boolean flags on the user's workspace role. | Category | Permission key | Description | | --------------- | ------------------- | --------------------------------------------------------- | | Flows | `flows_edit` | Create and edit workflows | | Flows | `flows_delete` | Delete workflows | | Flows | `flows_run` | Execute workflows | | Flows | `flows_advanced` | Advanced workflow features (API deployment, versioning) | | Agents | `agents_edit` | Create and edit AI agents | | Agents | `agents_delete` | Delete agents | | Agents | `agents_run` | Chat with and execute agents | | Agents | `agents_advanced` | Advanced agent features | | Knowledge Bases | `kbs_edit` | Create, upload, and manage documents | | Knowledge Bases | `kbs_delete` | Delete knowledge bases | | Knowledge Bases | `kbs_query` | Query and search knowledge bases | | Knowledge Bases | `kbs_advanced` | Advanced KB features (ingestion pipelines, etc.) | | Administration | `integrations_edit` | Connect and configure external integrations | | Administration | `users_edit` | Invite and modify workspace members | | Administration | `users_delete` | Remove members from the workspace | | Administration | `workspace_admin` | Full workspace administration (settings, roles, API keys) | ### `workspace_admin` cascade behavior `workspace_admin` is a superset of the other administration permissions. When a user or API key has `workspace_admin`, the authorization layer grants: * Full **integrations** access (create, read, edit, delete) — equivalent to `integrations_edit` plus create/delete. * Full **workspace users** access (create, read, edit, delete) — equivalent to `users_edit` plus `users_delete` plus create. `integrations_edit` alone grants read and edit on integrations. `users_edit` alone grants read and edit on workspace members. `users_delete` alone grants delete on workspace members. *** ## Organization-Level Permissions These permissions control tenant-wide operations and are checked independently of workspace membership. | Category | Permission key | Description | | ------------ | ------------------ | -------------------------------------------------- | | Users | `users_read` | View all users in the organization | | Users | `users_invite` | Invite new users to the organization | | Users | `users_edit` | Modify user information | | Users | `users_delete` | Remove users from the organization | | Workspaces | `workspace_read` | View all workspaces | | Workspaces | `workspace_write` | Create new workspaces | | Workspaces | `workspace_edit` | Modify workspace control | | Workspaces | `workspace_delete` | Delete workspaces | | Organization | `org_read` | View organization details | | Organization | `org_edit` | Modify organization details | | Organization | `org_billing` | Manage billing, subscriptions, and payment methods | | Organization | `org_admin` | Full organization admin access | | Settings | `settings_read` | View platform settings | *** ## Role Scope Workspace roles can be **global** or **workspace-scoped**: * **Global role** (`is_global=true`) — applies to every workspace the user belongs to. * **Workspace-scoped role** — applies only to the specific workspace the role was created for. Global roles are created from the **Roles → Workspace → All workspaces** view. Workspace-scoped roles are created for a specific workspace. *** ## Admin Configuration Role-to-permission mapping and global authorization policy should be managed from **Settings → Roles** by users with `workspace_admin` (for workspace roles) or `org_admin` (for organization roles). Keep role definitions small and composable. Use the individual permission keys as the stable contract rather than building monolithic admin roles. # Encryption Source: https://docs.noxus.ai/deployment/security/encryption Encryption at rest and in transit for Noxus deployments Noxus security should enforce encryption in two domains: * at rest (database, cache, storage, secrets) * in transit (client traffic and service-to-service traffic) ## At-Rest Encryption * PostgreSQL storage encryption (managed DB encryption or encrypted volumes) * Redis encryption where supported by your managed/runtime option * object storage encryption for cold storage buckets/containers * encrypted secret backends for credentials and keys ## In-Transit Encryption * TLS 1.2+ for all public endpoints * internal service encryption where required by policy * encrypted links to managed Postgres/Redis when available ```mermaid theme={null} flowchart LR U[Client] -- TLS --> IN[Ingress / Proxy] IN -- TLS --> FE[Noxus Frontend] IN -- TLS --> BE[Noxus Backend] BE -- TLS --> PG[(PostgreSQL)] BE -- TLS --> RD[(Redis)] BE -- TLS --> CS[(Cold Storage)] ``` ## Key Management * use dedicated secret managers or encrypted K8s secret workflows * rotate encryption/signing keys on a defined schedule * keep key access restricted to least privilege identities Treat key rotation and backup decryption testing as mandatory operational controls. Audit logging and operational controls Trust boundaries, secret handling, and the sandbox isolation model # Virtual Machine Source: https://docs.noxus.ai/deployment/vm/overview Deploy Noxus on a VM with Docker Compose, systemd, and supervisord Noxus provides a robust, single-node deployment path using Docker Compose, managed by a system service for high availability and health monitoring. ## System Architecture The VM deployment is built on three core layers: 1. **systemd**: A `systemctl` service manages the overall lifecycle of the Noxus stack. 2. **Docker Compose**: Orchestrates the containerized services (Frontend, Backend, Workers, etc.). 3. **supervisord**: Manages process-level execution, health checks, and automatic restarts within the environment. ## Hardware Guidelines For a production-grade VM deployment, we recommend the following minimum specifications: | Resource | Recommended | Minimum | | :---------- | :---------- | :---------- | | **vCPU** | 8 vCPU | 4 vCPU | | **RAM** | 32 GiB | 16 GiB | | **Storage** | 500 GiB SSD | 250 GiB SSD | All dependencies are either included in the pre-built image or automatically downloaded and installed during the setup process. *** ## Deployment Modes You can deploy Noxus on a VM using two primary methods, both supported by our [noxus-infra](https://github.com/noxus-ai/noxus-infra) repository. The easiest way to get started. Use the interactive shell script to provision your host and install all components. 1. Clone the `noxus-infra` repository. 2. Run the installer: `bash install.sh`. 3. Follow the terminal wizard instructions to configure your environment. Boot a VM using a pre-configured Noxus image provided by our team. 1. Boot the VM image. 2. Configure your environment variables in `/env.vm` (refer to the `noxus-infra` examples). 3. Restart the system service: `systemctl restart noxus`. *** ## Component Configuration ### Optional Services The VM stack is modular. You can choose to run the following services within the VM or connect to external managed instances: * **PostgreSQL**: Optional (can use external RDS/Cloud SQL). * **Redis**: Optional (can use external Elasticache/MemoryStore). * **Nginx**: Optional, but **ideally runs directly on the host** for simplicity and better performance as a reverse proxy. ### Security Warning **Do not expose Redis to the outside world.** If you are running Redis inside the VM, ensure it is only accessible via the local bridge network or bound to `127.0.0.1`. Exposing Redis ports (default `6379`) to the public internet is a significant security risk. ## Operations & Maintenance * **Service Management**: Use `systemctl status noxus` to check the health of the entire stack. * **Logs**: Access service logs via `journalctl -u noxus` or through the Docker Compose logs. * **Environment**: All core configuration is managed via the `/env.vm` file. Detailed breakdown of environment variables and secrets. Access deployment scripts and example configurations. # Plugin Architecture Source: https://docs.noxus.ai/developers/architecture How the Noxus plugin system works — from sandboxed workers to node execution Noxus runs plugin code inside **isolated sandboxes**, separate from the core platform. This keeps the platform stable — and safe — while letting plugins use any dependencies and run arbitrary logic. ## High-level overview ```mermaid theme={null} graph LR subgraph Platform BE[Noxus Backend] W[Worker] end subgraph Manager["Sandbox Manager"] SM[Multiplexing bridge] end subgraph Sandboxes["Per-plugin sandboxes"] P1["Plugin A worker"] P2["Plugin B worker"] end BE -- "install / uninstall" --> SM BE -- "execute node" --> SM W -- "execute node" --> SM SM --> P1 SM --> P2 ``` The **Noxus Backend** manages plugin records (install, uninstall, status) and provisions each plugin's sandbox. The **Backend** and every **Worker** call plugin nodes during execution. The **Sandbox Manager** bridges them to the plugin's worker process — and multiplexes their requests, so all platform processes share one warm worker per plugin. ## Sandboxed workers Each installed plugin gets its own sandbox. The platform provisions it once: 1. **Download** the plugin source (Git repo, upload, or marketplace) 2. **Upload** the source tree into the sandbox 3. **Install** its dependencies there, in a `uv` virtual environment 4. **Start** `noxus plugin worker` — a long-lived process speaking JSON-RPC over stdio The worker stays **warm**: the plugin's module graph is imported once, so each subsequent node execution is a single round trip rather than a process start. If the sandbox is ever reaped, the next call transparently re-provisions it. Because plugin code is untrusted, it runs with **no platform credentials** and typically no network route back to the platform. Anything it needs from the platform — reading or writing a file — it asks for over the same channel (see *File handling* below). ## Editing a plugin without reinstalling Because the venv lives in the sandbox and survives a worker restart, updating a plugin's code is cheap: sync the changed files into the sandbox and restart the worker. Dependencies are only reinstalled when the plugin's dependency list actually changes. This is what makes an edit-and-run loop practical. ## Plugin lifecycle ```mermaid theme={null} sequenceDiagram participant User participant Platform as Noxus Backend participant SM as Sandbox Manager participant P as Plugin Worker User->>Platform: Install plugin (git URL) Platform->>Platform: Create plugin record (INSTALLING) Platform->>SM: Create sandbox Platform->>SM: Upload source tree Platform->>SM: Install dependencies (uv) Platform->>SM: Start worker SM->>P: noxus plugin worker P-->>Platform: manifest (handshake) Platform->>Platform: status=RUNNING, register nodes, triggers & integrations ``` ### Status flow Plugins go through these statuses: | Status | Meaning | | ---------------- | --------------------------------------------------------------- | | `installing` | Being downloaded and set up | | `running` | Healthy, worker warm and serving requests | | `missing_config` | Needs configuration before it can work | | `restarting` | Being restarted (after update or error recovery) | | `error` | Failed — retried on the next reconciliation pass (with backoff) | | `uninstalling` | Being removed | | `uninstalled` | Fully removed | ## Plugin sources Plugins can be installed from multiple sources: | Source | Use case | | --------------- | -------------------------------------------------------------------------------------------------------------- | | **Git** | Point to a Git repository (public or private with token). Supports branch, commit, and subdirectory targeting. | | **Upload** | Upload a `.tar.gz` package directly through the UI. | | **Marketplace** | Install from the official [Noxus Plugins repository](https://github.com/Noxus-AI/noxus-plugins). | | **Local** | Copy from a local directory (development only). | ## How nodes execute through plugins When a workflow runs a node that belongs to a plugin, here's what happens: ```mermaid theme={null} sequenceDiagram participant W as Worker participant Core as Spotflow Core participant SM as Sandbox Manager participant P as Plugin Worker (in sandbox) W->>Core: Execute node "my_node" Core->>Core: Lookup node in registry Note over Core: Node is a RemoteNode (from plugin) Core->>SM: node.execute (JSON-RPC over WebSocket) SM->>P: Forward to the warm worker's stdin P->>P: Run node.call(ctx, **inputs) P-->>SM: Result SM-->>Core: Result Core-->>W: Node result ``` When a plugin is loaded, its manifest registers **dynamic remote node classes** in the platform's node registry. These remote nodes look like normal nodes to the workflow engine, but their `call()` is forwarded to the plugin's worker instead of executing locally. Each plugin runs as a long-lived **worker process inside its own sandbox**. The plugin's module graph is imported once and stays warm, so a node execution costs one round trip rather than a process start. The backend and every worker replica share that same warm worker through the sandbox manager, which multiplexes their requests over one channel per plugin. ## Plugin worker internals The plugin worker (`noxus plugin worker`, generated by the SDK) speaks line-delimited JSON-RPC over stdin/stdout. It answers: | Method | Purpose | | -------------------- | ------------------------------------------ | | `manifest` | Return the plugin manifest | | `validate_config` | Validate plugin configuration | | `node.config` | Get dynamic node configuration | | `node.execute` | Execute a node | | `trigger.poll` | Run one poll of a plugin trigger | | `integration.config` | Get integration configuration | | `integration.ready` | Check if integration credentials are valid | The SDK discovers the plugin class and wires these up automatically — you never write transport code. For local authoring, `noxus plugin serve` exposes the same operations over HTTP. Note that platform file access is unavailable when serving locally, since files live on the platform. ## File handling Plugins run inside a sandbox with no direct access to the platform's storage — and typically no network route back to it either. Instead, file operations travel back along the same channel the platform used to call the plugin: ```mermaid theme={null} graph LR P[Plugin Worker] -- "host.get_content / host.upload_file" --> SM[Sandbox Manager] SM -- "JSON-RPC callback" --> BE[Platform] ``` The platform services these callbacks in-process against its own storage. Each call carries a single-use token the platform minted, which is how it knows *which workspace* the request belongs to — a plugin can only read and write files in the workspace of the run it is executing for. Inside plugin code, you use the `File` class and the execution context's file helper — the SDK handles all the bridging transparently. ## Security model * Each plugin runs inside its **own sandbox** (gVisor/syd), with its own dependencies and filesystem * Plugin code has **no platform credentials** — the sandbox holds no API keys * File access is **scoped to the calling workspace** via a single-use token minted per call * Integration credentials are passed through the **execution context** per-request, not stored in the plugin * Plugin source downloads support **private Git repositories** with token auth What you can build with plugins Build and deploy a plugin step by step # GitOps Source: https://docs.noxus.ai/developers/gitops Sync a workspace's flows, agents, knowledge bases and other config to a Git repository — treat your Noxus configuration as code. **Beta — enabled per deployment.** Reach out to an administrator to turn GitOps on for your tenant. Behaviour and the on-disk format may still change. GitOps serialises a workspace's **configuration** to YAML in a Git repository and keeps the database and the repo in sync — in either direction. It lets you review config changes as pull requests, promote a workspace between environments, and keep an auditable, versioned backup of everything you've built. Only **config entities** sync. Runtime data — runs, conversation history, ingested documents — is never written to Git. ## What syncs Each Noxus entity is exported as one **artifact** (the `noxus/v4` versioned representation). GitOps operates over these artifact kinds, one directory per kind: | Kind | Repo directory | | ---------------- | ------------------ | | `flow` | `flows/` | | `flow_version` | `flows/versions/` | | `agent` | `agents/` | | `agent_version` | `agents/versions/` | | `knowledge_base` | `knowledge-bases/` | | `inbox` | `inboxes/` | | `file` | `files/` | | `connection` | `connections/` | | `trigger` | `triggers/` | | `deployment` | `deployments/` | | `secret` | `secrets/` | By default **every kind** is in scope. The per-kind toggle in the UI opts a kind *out*; you can also narrow scope with `includeSlugs` / `excludeSlugs`. **Secrets sync, their values don't.** For a `secret` — and the credential on a `connection` — only the entry travels to Git: its name, id, and how other artifacts reference it. The encrypted value never leaves the database. ## The artifact format Every artifact is a single YAML document — an *envelope* with a stable header and a kind-specific `spec`: ```yaml theme={null} apiVersion: noxus/v4 kind: flow schemaVersion: 1 metadata: slug: invoice-extractor-a1b2c3 name: Invoice Extractor id: 0b3d9e7c-1f42-4a8e-9c11-a1b2c3d4e5f6 description: Pulls totals and line items out of PDF invoices labels: {} spec: # kind-specific configuration (nodes, edges, prompts, settings…) ``` * **`metadata.id`** is the artifact's identity. The trailing hex on the `slug` (`…-a1b2c3`) is derived from that id, so renaming an entity never rebinds it to a different one. * **References** between artifacts (a trigger pointing at a flow, an agent using a knowledge base) are stored as ids inside `spec`, so the whole set stays internally consistent when it moves. ### Repository layout ``` / ├── .noxus/ │ └── manifest.yaml # export metadata ├── flows/ │ ├── invoice-extractor-a1b2c3.yaml │ └── versions/ │ └── invoice-extractor-a1b2c3.yaml ├── agents/ │ └── support-bot-9f0e1d.yaml ├── knowledge-bases/ ├── connections/ ├── triggers/ ├── deployments/ └── secrets/ ``` ## Enable and connect Before anyone can connect an account, an administrator registers an OAuth app so Noxus can speak to GitHub on a user's behalf. On GitHub, go to **Settings → Developer settings → OAuth Apps → New OAuth App**, set the **Authorization callback URL** to `https:///api/backend/integrations/oauth/callback`, and copy the generated **Client ID** and **Client Secret**. Enter both in the Noxus Admin panel for the GitHub provider, granting the `repo` scope — read **and** write, since GitOps pushes commits. See [Admin: connecting integration providers](/integrations/admin-setup) for the full walkthrough. If your deployment ships the Noxus-managed GitHub provider, you can skip this — no client ID or secret needed. GitLab is configured the same way; generic Git uses an access token (or username + password) instead of OAuth. Once the provider exists, each user connects their own account in **Settings → Integrations** (**GitHub**, **GitLab**, or **generic Git**). This credential is what GitOps uses to read and write the repo. Open **Settings → GitOps**, pick the connection, then set the repository (`owner/repo`) and branch (defaults to `main`). Select the artifact kinds, pick a **direction**, and set the **sync interval**. Save the configuration. Click **Sync now** for an immediate run, or let the scheduler pick it up. Use **Test** first to confirm the connection can read and write the repo. ## Direction and scheduling GitOps runs in one of three directions: | Direction | Behaviour | | ----------------------- | ------------------------------------------------------------ | | `db_to_git` *(default)* | Push workspace config out to the repo. | | `git_to_db` | Import config from the repo into the workspace. | | `bidirectional` | Sync both ways; `authoritative` (`db` or `git`) breaks ties. | Scheduling is controlled by `syncIntervalMinutes` (default `15`). A background worker runs every five minutes and syncs each **enabled** workspace whose interval has elapsed. Set the interval to `0` for **manual-only** — the scheduler skips the workspace, but **Sync now** still works. The `enabled` flag only gates the *scheduler*. A manual **Sync now** needs just a valid connection and repository. A bidirectional manual run returns two run records (one per direction); single-direction returns one. ## Per-artifact push and pull You don't have to sync the whole workspace. From the editor, the Git control on a flow or agent shows whether that artifact is linked, ahead, or behind the repo, and lets you **push** or **pull** just that one — optionally **with its dependencies** (e.g. pushing a flow can carry the knowledge bases and connections it references). ## The API All endpoints are scoped to a workspace (`group_id`). Mutating calls require the workspace **`integrations:edit`** permission; reads require membership. Every route returns `404` while `GITOPS_ENABLED` is off. | Method | Path | Purpose | | ------ | -------------------------------------------------------- | ---------------------------------------- | | `GET` | `/groups/{group_id}/git-sync/config` | Read the sync config | | `PUT` | `/groups/{group_id}/git-sync/config` | Update the sync config | | `POST` | `/groups/{group_id}/git-sync/test` | Test the connection can read/write | | `GET` | `/groups/{group_id}/git-sync/health` | Connection + repo access status | | `GET` | `/groups/{group_id}/git-sync/repos` | List repos the connection can see | | `POST` | `/groups/{group_id}/git-sync/dry-run` | Preview the changes a sync would make | | `POST` | `/groups/{group_id}/git-sync/run` | Trigger a full sync now | | `GET` | `/groups/{group_id}/git-sync/runs` | Last 50 sync runs | | `GET` | `/groups/{group_id}/git-sync/runs/{run_id}` | One run with its per-artifact changes | | `GET` | `/groups/{group_id}/git-sync/artifacts/{kind}/{id}` | Sync status of one artifact | | `POST` | `/groups/{group_id}/git-sync/artifacts/{kind}/{id}/push` | Push one artifact (`?with_dependencies`) | | `POST` | `/groups/{group_id}/git-sync/artifacts/{kind}/{id}/pull` | Pull one artifact (`?with_dependencies`) | This is the internal management API for the settings UI, not the public v1 API — it is excluded from the production OpenAPI schema and its shape can change while GitOps is in beta. Don't hard-code against it in production integrations yet. ## Health and run history `GET …/health` reports one of `healthy`, `connection_missing`, `connection_invalid`, or `repo_unreachable`, along with whether the repo `exists` and is `can_read` / `can_write`. If the connection breaks, sync degrades to local-only rather than erroring, and the failure is recorded as a categorised run instead of a 500. Every sync produces a **run** with a per-artifact **change** list (kind, slug, name, action, direction, and any error). Runs are the audit trail for GitOps — they're recorded in dedicated `git_sync_runs` / `git_sync_changes` tables, separate from the web audit log. Use **dry-run** to preview exactly what a sync would change before committing to it. ## Safety controls GitOps defaults are deliberately conservative. The knobs worth knowing: Before pushing, each artifact's `spec` is scanned for secret-like values. `block` (default) refuses the push, `warn` records a warning, `off` disables it. File payloads (base64 `content`) are skipped — they're the intended data and would always false-positive. `by_id` (default) matches artifacts only by their id, so importing can never silently bind, say, a credential to the wrong connection by name. Other strategies (`id_then_slug`, `by_slug`, `clone`) trade safety for flexibility. `writeMode` controls what an import does to a matched entity: `replace` (default) overwrites in place, `create` mints fresh ids, `version` appends a version row. `onMissingDependency` (`warn` / `fail`) decides what happens when an artifact references something not present. `onUpstreamDelete` (`ignore_warn` default, or `soft_delete`) governs what happens when something disappears upstream. `removeUntracked` is a **destructive mirror**: a git→db sync deletes in-scope database artifacts that are absent from the repo. Off by default — enable only when the repo is truly the source of truth. When two workspaces share one database, an imported artifact's canonical id may already exist elsewhere. `allowSameDeployment` clones it under a fresh local id instead of failing, and GitOps refuses cross-workspace references so tenants stay isolated. Generic-Git targets are also SSRF-guarded. ## Using GitOps in CI/CD Because the repository holds your configuration as reviewable YAML, you can wrap it in your normal delivery process: * **Review config as code** — enable `db_to_git`, and every flow/agent change lands as a Git diff you can open a pull request against. * **Promote across environments** — export from staging, then `git_to_db` into production (or `bidirectional` with `authoritative: git`) to roll a reviewed set of artifacts forward. `by_id` matching keeps entities stable across the hop. * **Back up and restore** — a scheduled `db_to_git` sync gives you a point-in-time, versioned snapshot of a workspace you can restore by pulling. Start with `db_to_git` and `secretPolicy: block` to get a safe, one-way export working first. Only move to `git_to_db` / `bidirectional` — and only enable `removeUntracked` — once you've confirmed the repo contents with a **dry-run**. # Creating Custom Nodes Source: https://docs.noxus.ai/developers/nodes/creating-nodes Extend Noxus by building custom workflow nodes Custom nodes allow you to extend Noxus with specialized functionality tailored to your specific needs. This guide covers everything you need to know to build, test, and deploy custom nodes. **Heads-up — this page describes the V1 "connector" model.** Noxus has moved to **V2 "connector-free" nodes**: a V2 node has no wired input/output connectors. Its inputs are **bindable config fields** — each takes a literal value or a `:var[node_id.output]` reference to an upstream output — and its outputs are declared in a single output schema. New nodes should use the V2 model. The connector API below still works for V1 flows. For plugin authors, see the [Plugins overview](/developers/plugins/overview) and [How plugins run](/developers/plugins/sandbox-execution) for the current V2 node shape (`BaseNodeV2`). ## Node Architecture ### BaseNode Class All nodes inherit from `BaseNode[ConfigType]`, a generic base class that provides the node lifecycle and interface. ```python theme={null} from spotflow.nodes.base import BaseNode, NodeCategory from spotflow.nodes.data_types import Connector, TypeDefinition from pydantic import BaseModel class MyCustomNode(BaseNode["MyCustomNodeConfig"]): # Node metadata node_name = "my_custom_node" title = "My Custom Node" category = NodeCategory.DATA color = "#4A90E2" image = "https://your-icon-url.com/icon.png" visible = True # Show in node palette # Input/output connectors inputs = [ Connector( key="input_text", label="Input Text", type_=TypeDefinition.text() ) ] outputs = [ Connector( key="output_text", label="Output Text", type_=TypeDefinition.text() ) ] # Main execution method async def call( self, ctx: ExecutionContext, input_text: str ) -> dict[str, str]: # Your logic here result = input_text.upper() return {"output_text": result} ``` ### Node Metadata **node\_name** (str, required) * Unique identifier for the node type * Use snake\_case convention * Must be globally unique across all nodes **title** (str, required) * Display name in UI * Use Title Case * Keep concise (2-4 words) **category** (NodeCategory, required) * Groups nodes in palette * Options: `AIText`, `Agent`, `Logic`, `Data`, `Integration`, `InputOutput`, `Utility` **color** (str, required) * Hex color code for node appearance * Use brand colors or category-standard colors **image** (str, required) * Icon URL (PNG/SVG) * Displayed in node palette and on canvas * Recommended size: 48x48px **visible** (bool, default: True) * Whether to show in node palette * Set to False for deprecated or internal nodes ## Defining Inputs and Outputs ### Connector Types **Single-Value Connector**: ```python theme={null} from spotflow.nodes.data_types import Connector, TypeDefinition inputs = [ Connector( key="text_input", label="Text Input", type_=TypeDefinition.text(), required=True # Must be connected or have value ) ] ``` **Variable Connector** (multiple named inputs/outputs): ```python theme={null} from spotflow.nodes.data_types import VariableConnector inputs = [ VariableConnector( key="variables", label="Variables", type_=TypeDefinition.text() ) ] # User can add multiple inputs: var1, var2, var3, etc. # Accessed in call() as: variables: dict[str, str] ``` **Variable Type-Size Connector** (different types): ```python theme={null} from spotflow.nodes.data_types import VariableTypeSizeConnector inputs = [ VariableTypeSizeConnector( key="inputs", label="Inputs" ) ] # User can add inputs with different types # Accessed as: inputs: dict[str, Any] ``` ### Data Types Noxus supports rich type definitions: ```python theme={null} # Basic types TypeDefinition.text() # String TypeDefinition.number() # Float or int TypeDefinition.boolean() # True/False # File types TypeDefinition.file() # Any file TypeDefinition.image() # Image file TypeDefinition.audio() # Audio file # Structured types TypeDefinition.json() # JSON object # Lists TypeDefinition.text(is_list=True) # List of strings TypeDefinition.file(is_list=True) # List of files ``` ### Optional Inputs ```python theme={null} inputs = [ Connector( key="optional_param", label="Optional Parameter", type_=TypeDefinition.text(), required=False # Can be left unconnected ) ] # In call(), check if provided: async def call(self, ctx, optional_param: str | None = None): if optional_param: # Use parameter pass else: # Use default behavior pass ``` ## Node Configuration Configuration fields allow users to customize node behavior without connections. ### Configuration Schema Define a Pydantic model for configuration: ```python theme={null} from pydantic import BaseModel, Field from spotflow.nodes.config_fields import ( ConfigText, ConfigBigText, ConfigSelect, ConfigToggle, ConfigNumberSlider ) class MyNodeConfig(BaseModel): api_key: str = Field( title="API Key", description="Your API key for the service", json_schema_extra=ConfigText() ) mode: str = Field( title="Mode", description="Processing mode", default="fast", json_schema_extra=ConfigSelect( options=["fast", "balanced", "quality"] ) ) temperature: float = Field( title="Temperature", description="Randomness in generation", default=0.7, json_schema_extra=ConfigNumberSlider( min=0.0, max=2.0, step=0.1 ) ) enable_cache: bool = Field( title="Enable Caching", description="Cache results for faster retrieval", default=True, json_schema_extra=ConfigToggle() ) ``` ### Configuration Field Types **ConfigText**: Single-line text input **ConfigBigText**: Multi-line textarea **ConfigRichTextVariables**: Rich text editor with variable insertion **ConfigSelect**: Dropdown selection **ConfigMultiSelect**: Multi-select dropdown **ConfigToggle**: Boolean switch **ConfigNumberSlider**: Numeric slider **ConfigDictList**: Key-value pair list **ConfigJsonSchemaBuilder**: JSON schema designer **ConfigModelSelect**: LLM model picker **ConfigToolsSelect**: Agent tool selector ### Dynamic Configuration Generate configuration options dynamically: ```python theme={null} @classmethod def get_config( cls, ctx: ExecutionContext, config: "MyNodeConfig" ) -> type["MyNodeConfig"]: # Fetch options from database/API async with ctx.db() as db: accounts = await db.execute( select(Account).filter_by(user_id=ctx.user.id) ) account_options = [acc.name for acc in accounts] # Update config schema class DynamicConfig(MyNodeConfig): account: str = Field( title="Account", json_schema_extra=ConfigSelect(options=account_options) ) return DynamicConfig ``` ## Implementing Node Logic ### The call() Method The `call()` method is where your node's logic executes: ```python theme={null} async def call( self, ctx: ExecutionContext, # Input parameters match connector keys input_text: str, number_input: float, optional_file: File | None = None ) -> dict[str, Any]: """ Node execution logic. Args: ctx: Execution context with access to DB, Redis, credentials, etc. input_text: Text from input connector number_input: Number from input connector optional_file: Optional file input Returns: Dictionary mapping output connector keys to values """ # Access configuration api_key = self.config.api_key mode = self.config.mode # Your logic here result = await process_data(input_text, mode, api_key) # Return outputs return { "output_text": result.text, "output_number": result.score } ``` ### Sync vs Async Nodes can be synchronous or asynchronous: **Async (Recommended)**: ```python theme={null} async def call(self, ctx, input_text: str) -> dict: result = await async_api_call(input_text) return {"output": result} ``` **Sync**: ```python theme={null} def call(self, ctx, input_text: str) -> dict: result = sync_processing(input_text) return {"output": result} ``` Use async for: * Database queries * External API calls * I/O operations Use sync for: * Pure computation * Simple transformations ### Execution Context The `ExecutionContext` provides access to platform resources: **Database Access**: ```python theme={null} async with ctx.db() as db: user = await db.get(User, ctx.user.id) # Perform database operations ``` **Redis Access**: ```python theme={null} redis = ctx.redis await redis.set("key", "value") value = await redis.get("key") ``` **Credentials**: ```python theme={null} # Access integration credentials credentials = await ctx.get_credentials(integration_name="google") access_token = credentials.access_token ``` **LLM Access**: ```python theme={null} # Use LLM providers llms = ctx.llms() response = await llms.generate( model="gpt-4o", prompt="Hello, world!" ) ``` **Embeddings**: ```python theme={null} embeddings = ctx.embeddings() vectors = await embeddings.embed(["text1", "text2"]) ``` **User/Group Info**: ```python theme={null} user = ctx.user # Current user group = ctx.group # Current workspace tenant = ctx.group.tenant # Organization api_key = ctx.api_key # If called via API ``` **Fingerprint** (Run Metadata): ```python theme={null} fingerprint = ctx.get_fingerprint() # Contains: user_id, group_id, run_id, etc. ``` ## Error Handling ### Raising Errors Raise exceptions to signal errors: ```python theme={null} from spotflow.nodes.exceptions import NodeExecutionError async def call(self, ctx, input_text: str) -> dict: if not input_text: raise NodeExecutionError("Input text cannot be empty") try: result = await external_api(input_text) except APIException as e: raise NodeExecutionError(f"API call failed: {e}") from e return {"output": result} ``` ### Continue on Error Users can configure nodes to continue on error. Your node should return default values: ```python theme={null} async def call(self, ctx, input_text: str) -> dict: try: result = await risky_operation(input_text) return {"output": result} except Exception as e: # If continue-on-error is enabled, this returns default return {"output": ""} # Empty string as default ``` ## Timeout Configuration Nodes can specify dynamic timeouts: ```python theme={null} def calculate_timeout( self, ctx: ExecutionContext, **inputs ) -> int: """ Calculate timeout in seconds based on inputs. Returns: Timeout in seconds """ # Example: Longer timeout for larger files file_input = inputs.get("file_input") if file_input: file_size_mb = file_input.size / (1024 * 1024) return int(60 + file_size_mb * 2) # 60s + 2s per MB return 300 # Default 5 minutes ``` ## List Handling Nodes automatically handle list iteration when a list output connects to a non-list input. **Option 1: Non-List Input (Automatic Iteration)**: ```python theme={null} inputs = [ Connector( key="text_input", label="Text Input", type_=TypeDefinition.text() # NOT a list ) ] # When list connects here, node executes once per item async def call(self, ctx, text_input: str) -> dict: # Receives single string, even if list upstream result = text_input.upper() return {"output": result} ``` **Option 2: List Input (Processes Entire List)**: ```python theme={null} inputs = [ Connector( key="text_list", label="Text List", type_=TypeDefinition.text(is_list=True) # List type ) ] # Node receives entire list async def call(self, ctx, text_list: list[str]) -> dict: # Process all items together results = [t.upper() for t in text_list] return {"output_list": results} ``` ## File Handling ### Reading Files ```python theme={null} from spotflow.models import File async def call(self, ctx, file_input: File) -> dict: # Read file contents content = await file_input.read_bytes() # Or get file path file_path = file_input.path # Access metadata filename = file_input.filename mime_type = file_input.mime_type size = file_input.size return {"output": process(content)} ``` ### Creating Files ```python theme={null} from spotflow.models import File async def call(self, ctx, text_input: str) -> dict: # Create file from text output_file = File.from_text( text=text_input, filename="output.txt", mime_type="text/plain" ) # Or from bytes output_file = File.from_bytes( content=b"...", filename="output.pdf", mime_type="application/pdf" ) return {"output_file": output_file} ``` ## Testing Custom Nodes ### Unit Tests ```python theme={null} import pytest from spotflow.nodes.test_utils import create_test_context @pytest.mark.asyncio async def test_my_custom_node(): # Create test context ctx = await create_test_context() # Create node instance node = MyCustomNode(config=MyCustomNodeConfig(api_key="test")) # Execute node result = await node.call(ctx, input_text="hello") # Assert results assert result["output_text"] == "HELLO" ``` ### Integration Tests ```python theme={null} from spotflow.flow.runner import online_runner from spotflow.models import WorkflowDefinition @pytest.mark.asyncio async def test_node_in_workflow(): # Create workflow with your node workflow_def = WorkflowDefinition( nodes=[ {"id": "input", "type": "input", ...}, {"id": "custom", "type": "my_custom_node", ...}, {"id": "output", "type": "output", ...} ], edges=[...] ) # Execute workflow result = await online_runner( workflow=workflow_def, inputs={"input": "test"}, context=ctx ) # Verify results assert result["output"] == "EXPECTED" ``` ## Registering Nodes Register your custom node with the node registry: ```python theme={null} from spotflow.registry import get_registry registry = get_registry() registry.nodes.register(MyCustomNode) ``` For plugin-based distribution: ```python theme={null} # In your plugin's __init__.py def register_plugin(): from spotflow.registry import get_registry from .nodes import MyCustomNode registry = get_registry() registry.nodes.register(MyCustomNode) ``` ## Best Practices ### Design **Single Responsibility**: Each node should do one thing well **Composability**: Design nodes to work together via connections **Clear Naming**: Use descriptive names for nodes, inputs, and outputs **Consistent Style**: Follow existing node conventions ### Performance **Async I/O**: Use async for network and database operations **Batch Operations**: Process batches efficiently when possible **Resource Limits**: Set appropriate timeouts for long operations **Memory Management**: Clean up large objects after use ### Error Handling **Descriptive Errors**: Provide clear error messages **Validation**: Validate inputs early **Graceful Degradation**: Return sensible defaults when possible **Logging**: Log errors with context for debugging ### Security **Input Validation**: Validate and sanitize all inputs **Credential Handling**: Never log or expose credentials **API Rate Limits**: Respect external API rate limits **Dependency Security**: Keep dependencies updated ## Advanced Topics ### Progress Updates Report progress for long-running operations: ```python theme={null} async def call(self, ctx, items: list[str]) -> dict: results = [] total = len(items) for i, item in enumerate(items): result = await process(item) results.append(result) # Update progress (0.0 to 1.0) await ctx.update_progress((i + 1) / total) return {"results": results} ``` ### Streaming Outputs Stream outputs for real-time updates: ```python theme={null} async def call(self, ctx, prompt: str) -> dict: full_response = "" async for chunk in llm_stream(prompt): full_response += chunk # Stream to UI await ctx.stream_output("response", chunk) return {"response": full_response} ``` ### Memory Nodes Access persistent memory: ```python theme={null} # Write to memory await ctx.write_memory("key", "value", scope="workflow") # Read from memory value = await ctx.read_memory("key", scope="workflow") ``` ## Example: Complete Custom Node ```python theme={null} from pydantic import BaseModel, Field from spotflow.nodes.base import BaseNode, NodeCategory from spotflow.nodes.data_types import Connector, TypeDefinition from spotflow.nodes.config_fields import ConfigText, ConfigToggle from spotflow.nodes.exceptions import NodeExecutionError from spotflow.integrations.credentials import ExecutionContext class WeatherNodeConfig(BaseModel): api_key: str = Field( title="API Key", description="OpenWeatherMap API key", json_schema_extra=ConfigText() ) use_celsius: bool = Field( title="Use Celsius", description="Temperature in Celsius instead of Fahrenheit", default=True, json_schema_extra=ConfigToggle() ) class WeatherNode(BaseNode[WeatherNodeConfig]): node_name = "weather_node" title = "Get Weather" category = NodeCategory.DATA color = "#4A90E2" image = "https://example.com/weather-icon.png" visible = True inputs = [ Connector( key="city", label="City", type_=TypeDefinition.text(), required=True ) ] outputs = [ Connector( key="temperature", label="Temperature", type_=TypeDefinition.number() ), Connector( key="description", label="Description", type_=TypeDefinition.text() ) ] def calculate_timeout(self, ctx, **inputs) -> int: return 30 # 30 seconds for API call async def call( self, ctx: ExecutionContext, city: str ) -> dict[str, float | str]: import httpx if not city: raise NodeExecutionError("City name is required") api_key = self.config.api_key units = "metric" if self.config.use_celsius else "imperial" try: async with httpx.AsyncClient() as client: response = await client.get( f"https://api.openweathermap.org/data/2.5/weather", params={ "q": city, "appid": api_key, "units": units } ) response.raise_for_status() data = response.json() return { "temperature": data["main"]["temp"], "description": data["weather"][0]["description"] } except httpx.HTTPError as e: raise NodeExecutionError(f"Weather API error: {e}") from e # Register the node from spotflow.registry import get_registry get_registry().nodes.register(WeatherNode) ``` *** Building custom nodes extends Noxus with unlimited possibilities. Start with simple nodes and gradually add complexity as you master the patterns. Deep dive into configuration field types and dynamic configuration # Creating Configurable Plugins Source: https://docs.noxus.ai/developers/plugins/configurable-plugins Plugin-level config, node config with NCL, and dynamic config Noxus renders configuration forms for you from typed schemas — no frontend code. There are three levels of configuration, all built with the **NCL** config language (`Parameter` + `display=` widgets) from `noxus_sdk.ncl`: * **Plugin-level config** — settings that belong to the whole plugin. * **Node config** — per-node settings, including bindable inputs. * **Dynamic config** — config computed server-side at edit time. ## Plugin-level configuration Plugin config lives on a `PluginConfiguration` subclass, which you pass as the type parameter of `BasePlugin[...]`. Its fields are `Parameter(...)` — the same NCL widgets you use everywhere. ```python theme={null} from noxus_sdk.ncl import ConfigNumber, ConfigText, ConfigToggle, Parameter from noxus_sdk.plugins import BasePlugin, PluginConfiguration from noxus_sdk.schemas import ValidationResult class WeatherPluginConfig(PluginConfiguration): units: str = Parameter(default="metric", display=ConfigText(label="Units")) api_timeout: int = Parameter(default=30, display=ConfigNumber(label="API timeout (s)")) enable_caching: bool = Parameter(default=True, display=ConfigToggle(label="Enable cache")) def validate_config(self) -> ValidationResult: if self.api_timeout < 1: return ValidationResult(valid=False, errors=["Timeout must be >= 1"]) return ValidationResult(valid=True) class WeatherPlugin(BasePlugin[WeatherPluginConfig]): name = "weather" display_name = "Weather" version = "1.0.0" description = "Weather nodes" author = "You" # nodes(), integrations(), ... ``` Override `validate_config` to gate the install. When a plugin declares config, the platform calls `validate_config` before moving the plugin to **running** — if it returns `valid=False`, or if config was never set, the plugin sits in **missing\_config** until an admin provides a valid configuration. ### Reading plugin config in a node Plugin config is delivered to every call on the execution context as `ctx.plugin_config` (a dict of the saved values): ```python theme={null} async def call(self, ctx: RemoteExecutionContext, city: str) -> dict: units = ctx.plugin_config.get("units", "metric") timeout = ctx.plugin_config.get("api_timeout", 30) return {"temp": 22 if units == "metric" else 72} ``` ## Node configuration with NCL Node config is a `NodeConfiguration` subclass; each field is a `Parameter(...)` with an optional `display=` widget. Values round-trip to the worker and reach the node as `self.config`. ```python theme={null} from noxus_sdk.ncl import ConfigNumber, ConfigSelect, ConfigToggle, Parameter from noxus_sdk.nodes.base import NodeConfiguration class ScraperConfig(NodeConfiguration): provider_from_input: bool = Parameter( default=False, display=ConfigToggle(label="Receive provider as input"), ) source: str = Parameter( default="acme", display=ConfigSelect(label="Source", values=["acme", "globex", "initech"]), ) max_pages: int = Parameter( default=5, display=ConfigNumber(label="Max pages"), ) ``` Common widgets from `noxus_sdk.ncl`: | Widget | Renders | | :----------------------------------- | :------------------------------------------------------------ | | `ConfigText` | single- or multi-line text (`is_textarea`, `number_of_lines`) | | `ConfigPassword` | masked secret input | | `ConfigNumber` / `ConfigNumberRange` | numeric input / bounded numeric input (`min`, `max`) | | `ConfigToggle` | boolean switch | | `ConfigSelect` / `ConfigMultiSelect` | single / multi choice (`values=[...]`) | | `ConfigDivider` | visual separator | Fields without a `display` still render with a default widget for their type. ### Conditional visibility with `VisibleIf` Show or hide a field based on another field's value with a `VisibleIf` rule (`noxus_sdk.ncl`) — evaluated in the form as the user edits, no round-trip: ```python theme={null} from noxus_sdk.ncl import VisibleIf source: str = Parameter( default="acme", display=ConfigSelect(label="Source", values=["acme", "globex"]), # Only shown when provider_from_input is False rules=[VisibleIf(config_source="provider_from_input", value=False)], ) ``` ### Bindable inputs (V2 nodes) On a **V2** node, mark a config field `Parameter(bindable=True)` to make it a bindable **input** — in the editor it accepts a literal or a `:var[...]` reference to an upstream output. It still arrives on `self.config`. See the [overview](/developers/plugins/overview) for the full V2 node shape. ## Dynamic configuration (`get_config`) To compute config server-side at edit time — e.g. fetch select options from an upstream API, or toggle field/connector visibility based on other values — override `get_config`. It runs in the worker; mutate the response and return it. ```python theme={null} from noxus_sdk.nodes.schemas import ConfigResponse class MyNode(BaseNode[MyConfig]): @classmethod async def get_config( cls, ctx: RemoteExecutionContext, response: ConfigResponse, skip_cache: bool = False, ) -> ConfigResponse: response.config_values["choice"] = ["alpha", "beta"] return response ``` The response exposes `config` (the serialized fields), `config_values` (the saved values), and — for V1 nodes — `inputs`/`outputs` you can flip `visible` on. The platform only wakes the worker for this when a `ConfigSelect` has no static `values`, so static nodes never pay the round-trip. ## Runtime Safety * **Validate at the boundary.** Use `validate_config` on plugin config and `NodeConfiguration` field types to reject bad input before it reaches your code. * **Sanitize before external calls.** Never interpolate raw config strings into shell commands, URLs, or queries unchecked. * **Mask secrets.** Any credential-like field must use `ConfigPassword` so it is hidden in the form. For true integration secrets, prefer an [integration credential](/developers/plugins/creating-integrations) over a config field. Package, version, and roll out configuration changes safely. # Creating data sources Source: https://docs.noxus.ai/developers/plugins/creating-datasources Author a knowledge-base data source in a plugin — pull files from an external system into a Noxus KB A **data source** is an external system a Knowledge Base ingests files from. A plugin data source lets you feed documents from a proprietary system, internal store, or niche SaaS into the KB pipeline. The platform owns the KB itself — chunking, embedding, retrieval — and your data source only answers one question: *which files should be added?* A data source's `fetch()` runs **inside the plugin's sandbox worker**, invoked over JSON-RPC (`datasource.fetch`). Read [How plugins run](/developers/plugins/sandbox-execution) for the execution model — network posture, logging, and especially the **file callbacks** that move bytes across the sandbox boundary. ## The interface A data source subclasses `BaseDataSource[ConfigType]` and implements `fetch`. ```python theme={null} from noxus_sdk.datasources import BaseDataSource, DatasourceConfiguration from noxus_sdk.ncl import Parameter, ConfigText from noxus_sdk.files import File from noxus_sdk.plugins.context import RemoteExecutionContext class MyDataSourceConfig(DatasourceConfiguration): folder: str = Parameter(default="root", display=ConfigText(label="Folder")) class MyDataSource(BaseDataSource[MyDataSourceConfig]): datasource_name = "MyDataSource" # unique id title = "My Data Source" description = "Ingest files from My System" async def fetch(self, ctx: RemoteExecutionContext) -> list[File]: helper = ctx.get_file_helper() files: list[File] = [] for name, content in _pull_from_my_system(self.config.folder): f = await helper.upload_file( file_name=name, content=content, # bytes content_type="text/plain", group_id=ctx.group_id, ) files.append(f) return files ``` ### Class attributes | Attribute | Meaning | | ----------------------- | ------------------------------------------------------------------------------------------ | | `datasource_name` | Unique identifier for the data source type (used for lookup and in the manifest). | | `title` / `description` | Shown in the KB "Add knowledge" UI. | | `image` | Optional icon URL. | | `integrations` | Credential types this data source needs (read via `ctx.get_integration_credentials(...)`). | | `supports_sync` | Reserved. Leave `False` (the default) — see [Sync model](#sync-model). | `ConfigType` is your `DatasourceConfiguration` subclass (a `NodeConfiguration`, so fields are `Parameter(...)` with optional `display=` widgets). It reaches your instance as `self.config`. ## `fetch` contract ```python theme={null} async def fetch(self, ctx: RemoteExecutionContext) -> list[File]: ``` `fetch` performs a **one-shot ingestion**: pull the files you want in the KB and return them as `File` descriptors. This is the "Add knowledge" flow — the user picks your data source, fills its config, and the platform ingests whatever `fetch` returns. The important rule: **you don't return bytes, you upload them.** Fetch each file's content, then persist it with the file helper: ```python theme={null} f = await ctx.get_file_helper().upload_file( file_name="report.pdf", content=pdf_bytes, content_type="application/pdf", group_id=ctx.group_id, ) ``` `upload_file` stores the bytes on the platform (over a host callback — the sandbox has no direct storage access) and returns a `File` descriptor. Return the list of descriptors; the platform then chunks and embeds them into the KB. The upload is workspace-scoped by the host, so pass `ctx.group_id` for the calling workspace. Raise from `fetch` to fail the ingestion with a user-visible message. ## Using credentials Most data sources talk to an authenticated system. Declare the credential type and read it in `fetch`: ```python theme={null} class MyDataSource(BaseDataSource[MyDataSourceConfig]): integrations = ["my_system"] async def fetch(self, ctx): creds = ctx.get_integration_credentials("my_system") client = MyClient(token=creds.get("token")) ... ``` The credential type comes from a `BaseIntegration` / `BaseCredentials` pair in the same plugin — see [Creating integrations](/developers/plugins/creating-integrations). ## Registering the data source Return your data source classes from the plugin's `datasources()` method: ```python theme={null} class MyPlugin(BasePlugin[MyPluginConfig]): ... def datasources(self) -> list[type[BaseDataSource]]: return [MyDataSource] ``` A plugin that provides a data source satisfies the "at least one node, integration, or data source" requirement on its own. Each data source is serialized into the manifest (`DatasourceDefinition`); regenerate `manifest.json` whenever its name or config changes. On the platform side a single generic **Plugin Datasource** node backs every plugin-provided data source and slots into the existing KB ingestion path — there's nothing extra to wire; the platform resolves your data source by name from the manifest and dispatches `datasource.fetch` to the worker. ## Sync model Only **one-shot `fetch`** is supported today. Incremental sync — where the platform's sync engine periodically polls the source for changes and adds/updates/removes documents (a `list`/`get`/`download` interface) — is a later phase. `supports_sync` is the reserved flag for it; leave it `False`. Until then, re-running "Add knowledge" is how content is refreshed. ## Testing Unit-test `fetch` directly with a stubbed file helper, and exercise the full ingestion end to end by installing the plugin against a running stack (the reference plugin under `tests/plugins/` includes a data source and its `run_plugin_e2e.py` driver ingests from it). `noxus plugin serve` also mirrors `datasource.fetch` for local development. ## Related The sandbox model and the file callbacks `fetch` relies on. The `File` model and the file helper in depth. # Creating Integrations Source: https://docs.noxus.ai/developers/plugins/creating-integrations Typed credentials and integrations for external APIs and systems Integrations are how a plugin authenticates to an external service. An integration is a **typed credentials schema** plus a small class that describes it; nodes then declare which integration they need and read its credentials at run time. Credentials are the one **per-workspace** part of the plugin model — each workspace connects its own, and the platform injects them into the node's execution context. ## The three pieces A `BaseCredentials` subclass with a `type` and one `Parameter` per field. Mask secrets with `ConfigPassword`. Override `is_ready()` to say when the credential is usable. A `BaseIntegration[YourCredentials]` subclass — `display_name` and `image`. Optionally override `is_ready` to probe the service for real. Set `integrations = {"": ["", ...]}` on the node, and read the injected credentials with `ctx.get_integration_credentials("")`. ## Defining credentials and the integration `BaseCredentials` and `BaseIntegration` come from `noxus_sdk.integrations.base`; the field widgets from `noxus_sdk.ncl`. The credential's `type` string is the id nodes bind to and the key the platform stores credentials under. ```python theme={null} from typing import ClassVar from noxus_sdk.integrations.base import BaseCredentials, BaseIntegration from noxus_sdk.ncl import ConfigPassword, ConfigText, Parameter class MyServiceCredentials(BaseCredentials): type: ClassVar[str] = "my_service" base_url: str = Parameter( display=ConfigText(label="API URL", placeholder="https://api.myservice.com"), ) api_key: str = Parameter( display=ConfigPassword(label="API Key", placeholder="Your API key"), ) def is_ready(self) -> bool: return bool(self.base_url and self.api_key) class MyServiceIntegration(BaseIntegration[MyServiceCredentials]): display_name = "My Service" image = "https://.../my-service-logo.png" ``` The integration's credential `type` is derived automatically from the credentials class, and the field widgets render the connect form in workspace settings. Return the integration from your plugin's `integrations()`: ```python theme={null} def integrations(self) -> list[type[BaseIntegration]]: return [MyServiceIntegration] ``` ### Live readiness checks `BaseIntegration.is_ready` defaults to "the fields validate and `credentials.is_ready()` is true". Override it (async) to probe the service, so readiness reflects real authorization rather than just filled-in fields — as the ClipOne integration does: ```python theme={null} from typing import Any class ClipOneIntegration(BaseIntegration[ClipOneCredentials]): display_name = "ClipOne" image = _IMAGE @classmethod async def is_ready(cls, creds: dict[str, Any] | None) -> bool: parsed = cls.get_credentials(creds) if parsed is None or not parsed.is_ready(): return False try: client = ClipOneClient(api_key=parsed.api_key, url=parsed.url, pre_auth_header=parsed.pre_auth) await client.check_connectivity() return True except Exception: return False ``` ## Binding an integration on a node A node lists the credentials it needs in `integrations` — a mapping of the credential `type` to the field names it consumes — and reads them from `ctx`. The platform shows a credential picker for that type on the node and injects the selected workspace credential into the call. ```python theme={null} from noxus_sdk.nodes.base import BaseNode from noxus_sdk.plugins.context import RemoteExecutionContext class MyServiceNode(BaseNode[MyServiceConfig]): node_name = "MyServiceFetch" title = "Fetch from My Service" integrations = {"my_service": ["api_key"]} async def call(self, ctx: RemoteExecutionContext, path: str) -> dict: creds = ctx.get_integration_credentials("my_service") api_key = creds.get("api_key", "") base_url = creds.get("base_url", "") # ... call the external API with httpx, curl_cffi, etc. return {"result": "..."} ``` A common pattern is a tiny helper that builds a client from the context, so every node shares one place that reads the credentials — the scrapers plugin does this for its shared Proxy API key: ```python theme={null} def api_key_from_ctx(ctx: RemoteExecutionContext) -> str: creds = ctx.get_integration_credentials("proxy_api") or {} return creds.get("api_key", "") ``` Read credentials from `ctx` at call time — never cache them across calls. They are per-workspace and may differ between runs. If a required credential is missing, raise `IntegrationFailedError` from `noxus_sdk.errors` so the user sees an actionable message. ## Development Flow Decide the fields (URL, key, tenant, ...). Mask every secret with `ConfigPassword`. Implement `is_ready()` on the credentials. A robust async client (e.g. `httpx`) with retries and typed parsing. Import heavy deps **inside** `call`/helpers, not at module top — manifest generation imports your module in a lightweight environment. Add nodes that bind the integration via `integrations={...}` and read `ctx`. Override `is_ready` to hit the real service so the workspace UI shows accurate connection status. ## Security & Isolation * **No platform credential enters the sandbox.** Only the workspace-scoped integration credentials the node declared are injected, and only for the calling workspace. * **Per-workspace boundary.** A plugin cannot read another tenant's credentials or files; the single-use call token scopes every callback to `ctx.group_id`. Add plugin-level config, node config, and dynamic config to your integration. # Creating triggers Source: https://docs.noxus.ai/developers/plugins/creating-triggers Author a polling trigger in a plugin — emit events that start workflow runs, carrying cursor state across polls A **trigger** starts a workflow run in response to an external event. A plugin trigger is a **polling trigger**: the platform calls it on an interval, and it returns any new events plus the state it wants to see on the next poll. The platform owns everything hard about triggers — **scheduling, state persistence, and routing events to workflow runs** — exactly as it does for built-in triggers. Your trigger only answers one question: *given my config and the state from last time, what's new?* Trigger `poll()` runs **inside the plugin's sandbox worker**, invoked over JSON-RPC (`trigger.poll`). Read [How plugins run](/developers/plugins/sandbox-execution) for the execution model — network posture, logging to `stderr`, and file callbacks all apply here too. ## The interface A trigger subclasses `BasePollingTrigger[ConfigType]` and implements `poll`. ```python theme={null} from noxus_sdk.triggers import BasePollingTrigger, TriggerConfiguration from noxus_sdk.ncl import Parameter, ConfigText from noxus_sdk.plugins.context import RemoteExecutionContext class TickConfig(TriggerConfiguration): label: str = Parameter(default="tick", display=ConfigText(label="Label")) class TickTrigger(BasePollingTrigger[TickConfig]): trigger_name = "MyTick" # unique id for this trigger type title = "Tick" description = "Emits a counter every interval" polling_interval = 60.0 # seconds between polls (default 300) outputs = {"message": "str", "tick": "number"} async def poll( self, ctx: RemoteExecutionContext, state: dict ) -> tuple[list[dict], dict]: tick = int(state.get("tick", 0)) + 1 events = [{"message": f"{self.config.label}-{tick}", "tick": tick}] new_state = {"tick": tick} return events, new_state ``` ### Class attributes | Attribute | Meaning | | ----------------------- | ------------------------------------------------------------------------------------------------------------ | | `trigger_name` | Unique identifier for the trigger type (used for lookup and in the manifest). | | `title` / `description` | Shown in the editor's trigger picker. | | `image` | Optional icon URL. | | `polling_interval` | Seconds between polls. Defaults to `300.0`. | | `outputs` | A `{field_name: type_label}` map. Each field becomes a workflow input the editor can wire from this trigger. | | `integrations` | Credential types this trigger needs (see below). | `ConfigType` is your `TriggerConfiguration` subclass (a `NodeConfiguration`, so its fields are `Parameter(...)` with optional `display=` widgets, just like a node's config). It reaches your instance as `self.config`. ## `poll` contract ```python theme={null} async def poll(self, ctx, state: dict) -> tuple[list[dict], dict]: ``` * **`state`** is whatever your previous `poll` returned as its second element (an empty dict on the first poll). Use it as a **cursor** — a last-seen id, a timestamp, a page token. * **Return `(events, new_state)`.** `events` is a list of **JSON-serializable dicts**; each dict's fields become the trigger inputs for one workflow run. Return `[]` when nothing is new. `new_state` is persisted by the platform and handed back on the next poll. Emit one event per thing that happened. The platform turns each event into a run; your `outputs` map tells the editor which fields the event carries so they can be bound to workflow inputs. A dropped connection mid-poll is **not retried** (see [at-most-once side effects](/developers/plugins/sandbox-execution#at-most-once-side-effects)). Advance your cursor in `new_state` only for events you actually returned, so a re-poll after a failure re-emits rather than skips. ## Using credentials If a trigger needs to authenticate to an external service, declare the credential type(s) it uses and read them from `ctx`: ```python theme={null} class TickTrigger(BasePollingTrigger[TickConfig]): integrations = ["my_weather"] async def poll(self, ctx, state): creds = ctx.get_integration_credentials("my_weather") api_key = creds.get("api_key") ... ``` The credential type is defined by a `BaseIntegration` / `BaseCredentials` pair in the same plugin — see [Creating integrations](/developers/plugins/creating-integrations). ## Registering the trigger Return your trigger classes from the plugin's `triggers()` method: ```python theme={null} class MyPlugin(BasePlugin[MyPluginConfig]): ... def triggers(self) -> list[type[BasePollingTrigger]]: return [TickTrigger] ``` Each trigger is serialized into the plugin **manifest** (`TriggerDefinition`) at packaging time. Regenerate `manifest.json` whenever a trigger's name, config, or `outputs` change. A plugin must provide at least one **node, integration, or data source** — triggers alone don't make a valid plugin. Ship a trigger alongside the node or integration it drives. On the platform side, an installed plugin trigger is resolved on demand from the plugin components table (a read-through in the trigger registry), so it works without being eagerly registered at startup. ## Testing * **Locally**, `noxus plugin serve` exercises your plugin's surfaces, **except `trigger.poll`** — polling is driven only by the platform, so the local dev server does not expose it. Test `poll` directly instead: ```python theme={null} import asyncio from noxus_sdk.plugins.context import RemoteExecutionContext from my_plugin import TickTrigger, TickConfig trigger = TickTrigger(TickConfig(label="t")) events, state = asyncio.run(trigger.poll(RemoteExecutionContext(), {})) assert events[0]["tick"] == 1 assert state == {"tick": 1} # feed the returned state back to prove the cursor advances events2, state2 = asyncio.run(trigger.poll(RemoteExecutionContext(), state)) assert state2 == {"tick": 2} ``` * **End to end**, install the plugin against a running stack and let the platform schedule the poll and create runs. The reference plugin in `tests/plugins/` and its `run_plugin_e2e.py` driver exercise a trigger this way. ## Related The sandbox execution model behind `trigger.poll`. Define the credentials a trigger reads from `ctx`. # Plugins Overview Source: https://docs.noxus.ai/developers/plugins/overview How Noxus plugins work — components, the manifest contract, and the sandboxed runtime Plugins extend Noxus without changing core platform code. A single plugin is an installable Python package built with the [`noxus-sdk`](https://pypi.org/project/noxus-sdk/) that can contribute **nodes**, **integrations with credentials**, **polling triggers**, and **knowledge-base data sources** — all versioned and deployed together. At install time the platform reads the plugin's **manifest** (reflected from your code), provisions an isolated sandbox, and starts a long-lived worker in it. At run time the platform dispatches component calls to that worker over JSON-RPC. You never register anything globally: the manifest is the contract. ## What a plugin can provide ```mermaid theme={null} mindmap root((Plugin)) Nodes V2 connector-free V1 edge connectors Dynamic config File transformations Integrations Typed credentials Readiness checks Per-workspace secrets Triggers Polling on an interval Cursor state across polls Data Sources Knowledge base ingestion ``` A plugin must declare at least one node, integration, trigger, or data source. Each component surfaces in the matching place in the product — nodes in the flow editor's palette, integrations in workspace settings, triggers in the trigger catalogue, data sources in the knowledge-base source picker. ### Nodes — V2 and V1 Nodes are the building blocks of workflows. Plugins provide two kinds, and a single plugin can mix them freely: Connector-free, mirroring the platform's native V2 nodes. A node is two schemas: a **config schema** whose fields are its settings (a field marked `bindable=True` becomes an input that takes a literal or a `:var[...]` reference), and an **output schema** that declares every output. Everything arrives on `self.config`. Edge-connector nodes for V1 flows. Inputs and outputs are `Connector` objects wired by edges; inputs arrive as keyword arguments to `call`. Use only when you specifically need a V1 flow. The SDK keeps V1 and V2 as **separate entities**: `BasePlugin.get_manifest()` splits the classes returned from `nodes()` into two manifest lists (`nodes` and `nodes_v2`) by `issubclass(n, BaseNodeV2)`. On the platform each kind is backed by its own **generic executor**, so there is no per-node synthesized class — the executor reads the component's shape from the stored manifest and dispatches by `(plugin, node_name)`. ### Integrations and credentials An integration manages **authentication and credentials** for an external service. You declare a `BaseCredentials` schema (its fields render in workspace settings) and a `BaseIntegration`; a node lists which credential types it needs and reads them at execution time. Credentials are the one part of the plugin model that is **per-workspace** — the platform stores and injects them scoped to the calling workspace. ### Polling triggers A trigger starts workflow runs from external events. A `BasePollingTrigger` answers one `poll` call on an interval and carries **cursor state** across polls; the platform keeps owning scheduling, state persistence, and event-to-run routing. ### Knowledge-base data sources A `BaseDataSource` feeds documents into a Noxus knowledge base. Its `fetch` returns `File`s (uploaded through the host callback), letting a KB ingest content from proprietary systems the built-in sources don't cover. ## The plugin class The core of a plugin is one `BasePlugin` subclass that declares its components: ```python theme={null} from noxus_sdk.plugins import BasePlugin, PluginConfiguration from noxus_sdk.plugins.types import PluginCategory from noxus_sdk.nodes.base import BaseNode from noxus_sdk.integrations.base import BaseIntegration from noxus_sdk.triggers import BasePollingTrigger class MyPluginConfig(PluginConfiguration): pass # optional plugin-level configuration fields class MyPlugin(BasePlugin[MyPluginConfig]): name = "my-plugin" # unique id (lookup, deps, DB) display_name = "My Plugin" version = "1.0.0" # semver description = "Does amazing things" category = PluginCategory.GENERAL author = "Your Name" def nodes(self) -> list[type[BaseNode]]: return [MyV2Node, MyLegacyNode] # V2 and V1 may mix def integrations(self) -> list[type[BaseIntegration]]: return [MyServiceIntegration] # optional def triggers(self) -> list[type[BasePollingTrigger]]: return [MyTrigger] # optional # def datasources(self) -> list[type[BaseDataSource]]: ... ``` ### A V2 node ```python theme={null} from noxus_sdk.nodes.base import BaseNodeV2, NodeConfiguration, NodeOutputs from noxus_sdk.ncl import Parameter, ConfigText from noxus_sdk.plugins.context import RemoteExecutionContext class EchoConfig(NodeConfiguration): text: str = Parameter(default="", bindable=True, display=ConfigText(label="Text")) suffix: str = Parameter(default="!") # plain config, not an input class EchoOutputs(NodeOutputs): echo: str tags: list[str] class EchoV2Node(BaseNodeV2[EchoConfig, EchoOutputs]): node_name = "MyEchoV2" title = "Echo (V2)" description = "Echoes text" async def call(self, ctx: RemoteExecutionContext) -> dict: return {"echo": f"{self.config.text}{self.config.suffix}", "tags": []} ``` Bindable config fields become the manifest's `inputs`; the rest is `config`. `call` returns a flat dict keyed by the output-schema field names (`list[X]` fields are list outputs). Configuration forms are rendered by the platform from your schema (the NCL config language) — no frontend code required. New work should be **V2**. V1 nodes remain supported for existing V1 flows, but V2 is the model the platform builds around going forward. ## The manifest is the contract `BasePlugin.get_manifest()` reflects over your plugin class into a `PluginManifest` — the single artifact the platform depends on. It carries: | Section | What it holds | | ------------------------------------------- | ----------------------------------------------------------------------------------------------- | | Metadata | `name`, `display_name`, `version`, `description`, `category`, `author` | | `dependencies` | your `pyproject.toml` `[project].dependencies`, so the platform can show what a plugin installs | | `config` | the serialized plugin-level NCL config schema | | `nodes` / `nodes_v2` | V1 and V2 node definitions, kept separate | | `integrations` / `triggers` / `datasources` | the remaining component definitions | The manifest is generated from code, **not** hand-written — regenerate it whenever a component signature or config changes: ```python theme={null} import json from my_plugin import MyPlugin json.dump(MyPlugin.get_manifest().model_dump(mode="json"), open("manifest.json", "w"), indent=2) ``` The platform stores the manifest in the database and **resolves components from it on demand** (a read-through lookup), rather than eagerly registering synthesized classes at boot. The manifest kept after install is the one reflected from the **live** worker, so it always matches the code actually running. ## How plugins run Your code never runs inside a platform process. Each plugin gets its **own sandbox** — its own filesystem, its own `uv` virtual environment, its own resource limits — and the platform starts a **warm worker** (`noxus plugin worker`) in it that imports your module graph once and stays alive between calls. The platform reaches it over line-delimited JSON-RPC. ```mermaid theme={null} graph LR subgraph Platform WF[Flow engine / worker] RT[Plugin runtime] end subgraph Sandbox PW["noxus plugin worker
(your code, warm)"] end WF -->|"node.execute / trigger.poll"| RT RT -->|JSON-RPC| PW PW -->|result| RT PW -.->|"host.get_content / host.upload_file"| RT ``` A workflow hitting a plugin node dispatches `node.execute` (with the node name, a serialized execution context, and the config) to the worker; the result comes back over the same channel. ### Files are host callbacks, not network calls A jailed sandbox has no route back to internal platform addresses. When your node reads or writes a `File`, the SDK issues a JSON-RPC **callback** on the same channel (`host.get_content` / `host.upload_file`) that the platform services **in-process** against its own database and storage. No platform credential ever enters the sandbox, and each callback is scoped by a single-use token tied to the calling workspace — so a plugin cannot reach another tenant's files. In `call`, use the execution context: ```python theme={null} async def call(self, ctx, raw): data = await raw.get_content(ctx) # download an input File out = await File.from_bytes(ctx, b"...", # upload a derived File name="out.txt", content_type="text/plain") creds = ctx.get_integration_credentials("my_service") # workspace credentials return {"file": out} ``` ### Isolation means * Your plugin can use **any Python dependencies** without conflicting with the platform. * A plugin crash or hang **can't take down** the core platform. * Plugins reach platform resources (files, credentials) only through the controlled callback surface. ### Sandbox providers and browser/JIT plugins The sandbox provider is a **deployment setting** (`SANDBOX_PROVIDER`), but it matters when authoring. The default provider, **`syd`**, uses a seccomp filter that `SIGSYS`-kills V8's JIT — so any browser or JIT-heavy workload (Playwright, Chromium, a Node driver) dies on startup, even when you only drive a *remote* browser and the local driver still needs to run. Those plugins must run under the **`gvisor`** provider (Linux/EKS) or **`none`** (local dev). Know this before shipping a browser-based plugin. ## Deployment model Plugins are **deployment-global**, not per-workspace: an org admin installs a plugin once and its components are available across the deployment. There is no per-workspace copy of a plugin. The only per-workspace piece is **integration credentials** — each workspace connects its own. ## Runtime gotchas The warm-worker sandbox imposes rules the local API doesn't. Keep these in mind: * **stdout is the protocol.** The worker speaks JSON-RPC on stdout; a stray `print` corrupts a call. Send logs to stderr, and wrap chatty dependencies with `contextlib.redirect_stdout(sys.stderr)`. * **Import heavy deps lazily, inside `call`.** Manifest generation imports your module in a lightweight environment; a top-level `import pandas`/`playwright` makes `get_manifest()` fail with `ModuleNotFoundError` even though the sandbox has the dependency. * **Degrade, don't raise, for expected external failures.** A raised exception fails the whole run. Catch flaky network/parsing errors and return an empty typed result; reserve exceptions (`IntegrationFailedError`) for genuine misconfiguration. * **Zip only source.** Exclude `.venv`, `__pycache__`, `.git` from the package; a stray `.venv` can balloon the archive from KB to tens of MB. * **Keep `noxus-sdk>=` aligned with the platform.** The sandbox resolves `noxus-sdk` alongside your deps; pinning an old floor can make the worker fail to start. ## SDK and CLI The `noxus-sdk` package provides the authoring surface: | Tool | Purpose | | ------------------------------------- | -------------------------------------------------- | | `BasePlugin` | Base class for the plugin definition | | `BaseNodeV2` / `BaseNode` | Base classes for V2 and V1 nodes | | `BaseIntegration` / `BaseCredentials` | Integrations and typed credentials | | `BasePollingTrigger` | Polling triggers | | `BaseDataSource` | Knowledge-base data sources | | `RemoteExecutionContext` | Runtime context: credentials, file helpers, config | | `noxus plugin create` | Scaffold a new plugin from a template | | `noxus plugin validate` | Validate structure and reflect the manifest | | `noxus plugin package` | Package the plugin as a `.tar.gz` for upload | `noxus plugin serve` runs a plugin as a local FastAPI app for development, but file operations raise there — there is no local host to service the callbacks. Nodes that read or write platform files can only be exercised under the platform runtime. ## Plugin lifecycle Scaffold with `noxus plugin create`, or set up the package structure by hand. Implement nodes, integrations, triggers, and configuration. Run `noxus plugin validate` to reflect the manifest and check your definitions. Push to Git or upload the `.tar.gz`, then install from the Noxus UI. The platform provisions the sandbox, installs your dependencies, and warms the worker. Monitor status, view worker logs, and update by restarting with a new source version. Step-by-step tutorial from zero to deployed plugin Plugin-level config, dynamic node config, and validation # Using Platform Resources Source: https://docs.noxus.ai/developers/plugins/platform-resources Files, email, credentials, and config a plugin can reach from its sandbox Your plugin code runs inside an isolated **sandbox worker**, not in a platform process. It has no route back to internal platform addresses and holds no platform credential. Everything it needs from the platform is handed to it through one object: the **`RemoteExecutionContext`** (`ctx`) passed to every `call` and `poll`. File and platform access happen as **JSON-RPC callbacks** on the worker's own channel, which the platform services **in-process** against its database and storage — so no key ever enters the sandbox. There is no `context.models`, `context.artifacts`, or HTTP-to-platform surface inside a plugin. The sandbox surface is exactly what's on `RemoteExecutionContext` below: files, integration credentials, plugin config, and the group id. ## The execution context Import it from `noxus_sdk.plugins.context`. Every node's `call(self, ctx, ...)` and every trigger's `poll(self, ctx, state)` receives one. | Attribute / method | Type | Description | | :-------------------------------------- | :---------------- | :----------------------------------------------------------------------------------------------------- | | `ctx.group_id` | `str \| None` | The tenant/workspace (group) id for this call. | | `ctx.plugin_config` | `dict` | The plugin-level config values (see [Configurable Plugins](/developers/plugins/configurable-plugins)). | | `ctx.integration_credentials` | `dict[str, dict]` | Credentials the platform injected, keyed by integration type. | | `ctx.get_integration_credentials(type)` | `-> dict` | Safe accessor for one integration's credentials (`{}` if absent). | | `ctx.get_file_helper()` | `-> FileHelper` | File I/O over host callbacks (download / upload). | | `ctx.call_token` | `str \| None` | Opaque, single-use token the host uses to scope callbacks. Plugin code never reads or sets it. | ```python theme={null} from noxus_sdk.plugins.context import RemoteExecutionContext async def call(self, ctx: RemoteExecutionContext, url: str) -> dict: creds = ctx.get_integration_credentials("my_service") # per-workspace secrets timeout = ctx.plugin_config.get("api_timeout", 30) # plugin-level config api_key = creds.get("api_key", "") ... ``` ## Files — host callbacks, no egress A `File` input arrives as a `File` model; return a `File` (or `list[File]`) to emit one. Reading or writing a file's bytes issues a JSON-RPC callback (`host.get_content` / `host.upload_file`) on the worker channel. The platform services these in-process, scoped by the single-use call token to the calling workspace — a plugin **cannot** read or plant files in another tenant. Use the file helper directly, or the higher-level `File` helpers that wrap it: ```python theme={null} from noxus_sdk.files import File, persist_files_locally async def call(self, ctx: RemoteExecutionContext, raw: File) -> dict: # Download an input File's bytes data = await raw.get_content(ctx) # Persist derived bytes and return a File output out = await File.from_bytes( ctx, data, name="processed.csv", content_type="text/csv" ) # When a library needs real paths on disk rather than File refs folder = await persist_files_locally(ctx, [raw]) return {"file": out} ``` The lower-level helper (`ctx.get_file_helper()`) exposes `get_content(file)` and `upload_file(file_name=, content=, content_type=, group_id=)` if you need them, but `File.from_bytes(...)` / `raw.get_content(ctx)` cover the common cases. `noxus plugin serve` (local dev) has no host to service file callbacks, so file operations raise there. Nodes that read or write platform files can only be exercised under the platform runtime. ## Email `noxus_sdk.email.Email` parses a raw email into a typed object with a markdown body and its attachments/inline images persisted as `File`s. ```python theme={null} import email from noxus_sdk.email import Email async def call(self, ctx: RemoteExecutionContext, raw: File) -> dict: msg = email.message_from_bytes(await raw.get_content(ctx), policy=email.policy.default) parsed = await Email.from_email_object(ctx, msg) return {"text": parsed.to_text(), "files": parsed.attachments} ``` `.to_text()` renders the email for LLM/tool input; pass `zip_attachments=True` to collapse attachments into a single zip. ## Integration credentials Credentials are the one **per-workspace** part of the plugin model — each workspace connects its own. A node declares which integration types it needs and reads them from the context at run time. See [Creating Integrations](/developers/plugins/creating-integrations) for the full authoring flow. ```python theme={null} class GetWeatherNode(BaseNode[EmptyConfig]): integrations = {"my_weather": ["api_key"]} # platform injects a credential picker async def call(self, ctx: RemoteExecutionContext, city: str) -> dict: creds = ctx.get_integration_credentials("my_weather") return {"used_key": creds.get("api_key", "MISSING")} ``` ## Typed errors Raise `IntegrationFailedError` or `UnexpectedError` from `noxus_sdk.errors` — not a bare `Exception` — when an upstream API or credential fails; the message reaches the node's failure UI. ```python theme={null} from noxus_sdk.errors import IntegrationFailedError if not ctx.get_integration_credentials("my_service").get("api_key"): raise IntegrationFailedError("No API key connected — connect the integration.") ``` The exception **type** does not cross the sandbox boundary today — only the message does. Make messages actionable, since that string is all the user sees. ## Best Practices Treat file access as I/O over the callback channel. Return `File` outputs rather than raw bytes so the platform tracks storage and lineage. Use `File.from_bytes` for derived content and `persist_files_locally` only when a library truly needs a filesystem path. Read credentials from `ctx.get_integration_credentials(type)` at call time — never cache them across calls, since they are per-workspace and can change. Fail with `IntegrationFailedError` when a required credential is missing. Pass only what a node needs. `ctx.group_id` scopes work to the current workspace; do not attempt to reach other groups — the call token overrides any `group_id` you pass to a callback anyway. Degrade for expected external flakiness (return an empty typed result), and reserve raised errors for genuine misconfiguration. A raised exception fails the whole run. Build typed credentials and connect nodes to external systems. # Publishing & Versioning Source: https://docs.noxus.ai/developers/plugins/publishing-versioning Packaging, the manifest, installing, versioning, and air-gapped deploys A plugin ships as an archive the platform reads at install time. This page covers what goes in the archive, how to install and update it, how versioning works, and how deployments without internet egress resolve dependencies. ## What's in the package At the **archive root** the platform needs exactly three things: ``` my_plugin/__init__.py # the plugin + its nodes / integrations / triggers pyproject.toml # name, version, dependencies (incl. noxus-sdk); packages = ["my_plugin"] manifest.json # generated from the code (never hand-written) ``` The upload installer reads `manifest.json` from the archive and fails without it, so it must be present and current. **Zip only source.** Exclude `.venv`, `__pycache__`, `.git`, `.ruff_cache`, and `node_modules`. A stray `.venv` — easy to create by running `uv` inside the plugin dir — can balloon the archive from a few hundred KB to tens of MB. ### Regenerate the manifest The manifest is **reflected from your code**, not written by hand. Regenerate it whenever a node, trigger, integration, or config signature changes: ```python theme={null} import json from my_plugin import MyPlugin json.dump(MyPlugin.get_manifest().model_dump(mode="json"), open("manifest.json", "w"), indent=2) ``` `noxus plugin package` and `noxus plugin generate-manifest` do this for you (see below). The manifest now also carries the plugin's **`dependencies`** — captured from your `pyproject.toml` `[project].dependencies` at generation time — so the platform can display what the plugin will install. ## Versioning Noxus uses semantic versioning, defined once in `pyproject.toml` and mirrored into the manifest at generation time: ```toml theme={null} # pyproject.toml [project] name = "weather-plugin" version = "1.2.0" # Major.Minor.Patch dependencies = [ "noxus-sdk>=0.5.1", # keep the floor at/above the platform's SDK version "httpx>=0.27", ] ``` ### SDK version alignment (required) The sandbox resolves `noxus-sdk` alongside your other dependencies. If you pin an **old** SDK, it can win the resolution and the worker fails to start with `No module named 'noxus_sdk.plugins.worker'`. Always depend on `noxus-sdk` at or above the platform's version (the platform installs a floor of at least `noxus-sdk>=0.5.1`), never an old floor. ## Release Strategy Confirm structure, definitions, and config schemas, and reflect the manifest. ```bash theme={null} noxus plugin validate --path ./my-plugin ``` Regenerate the manifest and build the archive. `noxus plugin package` produces a `.tar.gz`; the platform's upload flow also accepts `.zip`. ```bash theme={null} noxus plugin package --path ./my-plugin --output my-plugin-1.2.0.tar.gz ``` Install into your Noxus deployment via one of the sources below. ## Installing and updating Open **Settings → Plugins → Add plugin** and choose a source: Push the tagged release to your repository, then point the platform at it (branch / commit / subpath as needed). Drag the `.tar.gz` (or `.zip`) built by `noxus plugin package` into the upload tab. Open a PR to [github.com/Noxus-AI/noxus-plugins](https://github.com/Noxus-AI/noxus-plugins) so the version is discoverable from the in-app marketplace. On install the platform reads the manifest from the source, provisions the plugin's sandbox, installs its dependencies, warms the worker, and reflects the manifest from the **live** worker. To pick up a new version of an already-installed plugin, use **Reload** on its row — the platform re-downloads the source and provisions a fresh sandbox. ## Air-gapped / offline installs Because the sandbox installs a plugin's dependencies at provision time, a deployment with no internet egress needs those wheels available locally. The platform can serve them from a **baked wheelhouse** built into the sandbox image: | Setting | Default | Meaning | | :----------------------- | :------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `PLUGIN_WHEELHOUSE_DIR` | `/opt/plugin-wheels` | Directory of pre-baked wheels. When present, deps install from it via `--find-links`. Ignored (falls back to PyPI) if the dir is absent, so it's safe to leave on. | | `PLUGIN_INSTALL_OFFLINE` | `false` | Air-gapped mode: install **only** from the wheelhouse, never PyPI. A plugin whose dependency isn't baked fails fast instead of reaching out. | | `PLUGIN_SDK_REQUIREMENT` | `noxus-sdk>=0.5.1` | The SDK spec installed into each sandbox; guarantees the worker entrypoint exists. | With a wheelhouse present but `PLUGIN_INSTALL_OFFLINE` off, deps resolve from the wheelhouse first and fall back to PyPI for anything not baked. For a fully air-gapped deploy, bake every dependency (including `noxus-sdk`) into `PLUGIN_WHEELHOUSE_DIR` and set `PLUGIN_INSTALL_OFFLINE=true`. ## Compatibility Checklist * **Inputs/Outputs**: never remove or rename existing node inputs/outputs in a minor version; only add optional ones. * **Config migration**: give new config fields defaults so existing installs keep working. * **Dependencies**: keep the `noxus-sdk` floor aligned with the platform; for air-gapped targets, confirm new deps are in the wheelhouse before shipping. * **Deprecation**: mark old nodes as deprecated before removing them in a major version. Return to the full plugin development map. # How plugins run Source: https://docs.noxus.ai/developers/plugins/sandbox-execution Plugin nodes, triggers, and data sources execute inside an isolated per-plugin sandbox — how the warm worker, JSON-RPC transport, and host callbacks fit together Everything your plugin runs — node `call()`, trigger `poll()`, data source `fetch()`, dynamic `get_config()`, and credential checks — executes **inside an isolated sandbox**, never in the platform process. This page explains that execution model and what it means for you as a plugin author. This is a change from earlier versions of Noxus, where plugins ran as a local FastAPI "plugin server" process. There is no plugin server anymore — each plugin runs as a long-lived worker inside its own sandbox. ## Why the sandbox Plugin code is **untrusted, third-party code** that the platform installs and runs on behalf of every workspace that uses the plugin's nodes. Running it in-process would give it the platform's database handles, credentials, and internal network. Instead each plugin runs in the same isolated environment agents use for code execution — a [gVisor](https://gvisor.dev) sandbox provisioned by the sandbox manager. Each plugin gets its **own sandbox** with its own filesystem and its own Python virtual environment. A plugin's dependencies never collide with the platform's or with another plugin's. Private and internal network ranges are blocked from inside the sandbox. Plugin code can reach the public internet (to call third-party APIs) but **cannot reach the platform's internal services**. The sandbox holds no platform API key. It cannot call back into the platform except through the narrow, host-mediated file callbacks described below. A plugin that hangs or crashes takes down only its own worker. Calls are bounded by a timeout and the platform reconnects on the next request. The plugin system **requires the remote sandbox manager**. Plugins run as warm workers inside per-plugin sandboxes, so if `SANDBOX_MANAGER_URL` is not configured, plugins are unavailable — the local in-worker sandbox fallback (Deno/subprocess) does not host plugins. See the operator guide, [Sandbox configuration](/deployment/configuration/sandbox), for how the backend is selected and deployed. ## The warm worker When a plugin is installed the platform **provisions one sandbox for it** and starts a single long-lived process inside — `noxus plugin worker`. That process imports your plugin's module graph **once** and then waits for requests. ```mermaid theme={null} graph LR BE[Backend / Worker replicas] -->|"JSON-RPC over WS"| MGR[Sandbox manager] MGR -->|"stdio"| W["noxus plugin worker
(your plugin, imported once)"] W -.->|"host.get_content / host.upload_file
(callbacks)"| BE ``` Every platform process — the backend and each worker replica — connects to that **same** warm process through the sandbox manager's multiplexed WebSocket route and speaks **line-delimited JSON-RPC** to it. Requests are dispatched by method name: | Method | Runs | | --------------------------------------------- | ------------------------------------- | | `node.execute` | a node's `call()` | | `node.config` | a node's dynamic `get_config()` | | `trigger.poll` | a trigger's `poll()` | | `datasource.fetch` | a data source's `fetch()` | | `integration.ready` / `integration.config` | credential checks | | `manifest` / `list_nodes` / `validate_config` | metadata (also the install handshake) | Because the process stays warm, imports and any module-level state are paid for **once**, not per request. `stdout` carries only protocol frames; **anything your code writes to `stdout` will corrupt the channel** — write logs to `stderr` (loguru's default) or use `print(..., file=sys.stderr)`. Worker `stderr` is surfaced as plugin logs in the UI. ## Provisioning The first time a plugin's worker is needed, the platform provisions the sandbox under a cross-instance lock so replicas don't race: The plugin package is fetched host-side (Git, upload, or marketplace source). The plugin directory is streamed into the sandbox (to `/tmp/noxus-plugin`) as a gzipped tar. `uv` installs the plugin (and the `noxus-sdk` that provides the worker CLI) into a sandbox-local `.venv`. Your `pyproject.toml` dependencies are installed here. The platform connects and calls `manifest` to confirm the worker imported cleanly and is warm. The sandbox is **shared** across all platform processes and all tenants that use the plugin — provisioning happens once, not per call and not per workspace. The sandbox id is stored on the plugin row so any replica can re-attach to a live sandbox instead of provisioning a duplicate. Your plugin's dependencies must resolve a compatible `noxus-sdk` — the SDK that ships the `noxus plugin worker` command. If your `pyproject.toml` pins an older SDK, install fails fast with a clear error rather than timing out at the handshake. Keep your floor at or above the platform's required SDK version. ## How inputs and outputs cross the boundary Everything a call needs is serialized into the JSON-RPC request: the node/trigger/datasource name, the resolved config and inputs, and a `RemoteExecutionContext` (`ctx`). Your `call()`/`poll()`/`fetch()` returns plain JSON, which becomes the node's outputs or the trigger's events. **Files are the exception.** File bytes are never inlined into the request. Instead, `ctx.get_file_helper()` gives you a helper whose operations are serviced as **callbacks over the same channel**: * `await helper.get_content(file)` → issues a `host.get_content` request back to the platform, which reads the file from its own storage and returns the bytes. * `await helper.upload_file(...)` → issues a `host.upload_file` request; the platform writes the bytes to its storage and DB and returns a file descriptor you can return as an output. This is why the design works even though the sandbox cannot reach internal services: the plugin never HTTP-calls the platform and never holds a key. The callbacks run **in-process on the platform**, against its own database and storage. ### Workspace scoping is enforced by the host A plugin runs for **every** tenant that uses its nodes, so a file operation must be pinned to the workspace that made the call. Plugin code cannot be trusted to declare its own workspace — it could simply lie. Instead: * The host mints an **unguessable single-use token** for each call and places it in the `ctx`. Plugin code never reads or sets it. * Every file callback must present that token; the host maps it back to the calling workspace and refuses callbacks without a valid token. * On upload, the workspace resolved from the token **wins** over any `group_id` the plugin passes, so a plugin cannot plant files in another workspace. ## At-most-once side effects If the connection to a worker drops mid-call (a manager restart, a reaped sandbox), the platform **retries only read-only methods** (`manifest`, `node.config`, `integration.*`, …). It deliberately **does not retry `node.execute` or `trigger.poll`**: a dropped connection can't tell whether the call already ran, and re-sending could send the same email or create the same ticket twice. Such a call fails with a clear error instead of silently doubling. Each call is also bounded by a timeout (`PLUGIN_CALL_TIMEOUT`, 240s by default). A slow node should fit inside it; a hung or malicious worker cannot block a request forever. ## What this means for you * **Write to `stderr`, never `stdout`.** `stdout` is the protocol channel. * **Declare every dependency in `pyproject.toml`.** It is installed into the sandbox venv; nothing from the platform environment leaks in. * **Reach external services over the public internet** as normal (`httpx`, SDKs, …). Do not expect to reach anything on a private/internal address. * **Move file bytes through the file helper**, not by trying to read platform storage yourself. * **Make node/trigger work idempotent where you can**, since a mid-flight connection loss will not be retried for you. * **Keep module import cheap.** It happens once per worker start, but a slow import delays the first call and every restart. ## Related How the sandbox backend is chosen and deployed — gVisor, MicroVM, and the local fallbacks. The same isolation model, used by agents to run code and build artifacts. Author a polling trigger that emits events from inside the sandbox. Author a knowledge-base data source that ingests files from inside the sandbox. # 6. Advanced Techniques Source: https://docs.noxus.ai/developers/plugins/tutorial/advanced-techniques Config UI controls, dynamic config, list handling, error handling, and deployment This is **part 6** of the [Your First Plugin](/developers/plugins/your-first-plugin) tutorial. Make sure you've completed [5. Working with Files](/developers/plugins/tutorial/working-with-files) first. ## Node configuration with UI controls Add configuration fields that appear in the node's settings panel in the editor: ```python theme={null} from noxus_sdk.ncl import ( ConfigSelect, ConfigToggle, ConfigNumberSlider, Parameter, ) class GetWeatherConfig(NodeConfiguration): units: str = Parameter( default="metric", description="Choose temperature units", display=ConfigSelect(label="Temperature Units", values=["metric", "imperial", "kelvin"]), ) include_humidity: bool = Parameter( default=False, description="Also return humidity data", display=ConfigToggle(label="Include Humidity"), ) ``` Every display widget requires a **`label`** — it has no default. The `description` on `Parameter` is optional helper text; the `label` on the widget is what renders as the field name. Access config values in your node's `call()`: ```python theme={null} async def call(self, ctx: RemoteExecutionContext) -> dict: # Both config fields and bindable inputs are read from self.config. units = self.config.units # "metric", "imperial", or "kelvin" include_humidity = self.config.include_humidity # ... ``` ### Available config display types Every widget takes a `label`; the argument column shows what else each accepts. | Type | Description | | ------------------------------------------------------ | --------------------------------- | | `ConfigText(label=...)` | Single-line text input | | `ConfigBigText(label=...)` | Multi-line textarea | | `ConfigPassword(label=...)` | Masked secret input | | `ConfigSelect(label=..., values=[...])` | Dropdown select | | `ConfigMultiSelect(label=..., values=[...])` | Multi-select dropdown | | `ConfigToggle(label=...)` | Boolean switch | | `ConfigNumber(label=...)` | Number input | | `ConfigNumberSlider(label=..., min=, max=, step=)` | Slider | | `ConfigFile(label=...)` / `ConfigFileArray(label=...)` | File / file-list input | | `ConfigRichTextVariables(label=...)` | Rich text with variable insertion | ## Dynamic configuration Override `get_config()` to compute config server-side at edit time — for example, fetching a select's options from an upstream API. It runs in the plugin's worker; mutate the `ConfigResponse` and return it. Write dynamic options into `config_response.config_values` keyed by the field name: ```python theme={null} from noxus_sdk.nodes.schemas import ConfigResponse from noxus_sdk.plugins.context import RemoteExecutionContext class GetWeatherNode(BaseNodeV2[GetWeatherConfig, GetWeatherOutputs]): # ... @classmethod async def get_config( cls, ctx: RemoteExecutionContext, config_response: ConfigResponse, *, skip_cache: bool = False, ) -> ConfigResponse: # e.g. fetch the available options from an API using the connected # credentials, then populate the "region" dropdown dynamically. config_response.config_values["region"] = ["eu-west-1", "us-east-1"] return config_response ``` The platform only wakes the worker for `get_config` when a `ConfigSelect` has **no static `values`**. Nodes with fully static config never pay the round-trip, so add `get_config` only when options genuinely depend on context. ## Polling triggers A plugin can also emit events on a schedule with `BasePollingTrigger`. The platform owns scheduling, state persistence, and turning events into runs — your trigger only implements `poll(ctx, state)`, returning `(events, new_state)`. Cursor state round-trips between polls so you emit each event once: ```python theme={null} from noxus_sdk.triggers import BasePollingTrigger, TriggerConfiguration from noxus_sdk.plugins.context import RemoteExecutionContext class TickConfig(TriggerConfiguration): pass class TickTrigger(BasePollingTrigger[TickConfig]): trigger_name = "weather_tick" title = "Weather Tick" description = "Emits an incrementing tick on an interval" polling_interval = 60.0 # seconds # event field name -> human-readable type label outputs = {"message": "str", "tick": "number"} async def poll( self, ctx: RemoteExecutionContext, state: dict ) -> tuple[list[dict], dict]: tick = int(state.get("tick", 0)) + 1 events = [{"message": f"tick-{tick}", "tick": tick}] return events, {"tick": tick} ``` Register triggers on the plugin the same way as nodes and integrations: ```python theme={null} class WeatherPlugin(BasePlugin[WeatherPluginConfig]): # ... def triggers(self): return [TickTrigger] ``` Each event dict's fields become the workflow's trigger inputs (matching the `outputs` you declared). A trigger can also declare `integrations = ["weather_api"]` to require a connected credential. ## List handling When a list output connects to a **non-list** input, the platform runs the node once per item automatically. To process the whole list in a single run, declare the input as a list instead. On a V2 node that's a bindable `list[...]` config field: ```python theme={null} class SummariseCitiesConfig(NodeConfiguration): cities: list[str] = Parameter( default_factory=list, bindable=True, display=ConfigMultiSelect(label="Cities", values=[]), ) class SummariseCitiesOutputs(NodeOutputs): results: list[str] class SummariseCitiesNode(BaseNodeV2[SummariseCitiesConfig, SummariseCitiesOutputs]): node_name = "SummariseCitiesNode" title = "Summarise Cities" description = "Fetches weather for every city in one run" async def call(self, ctx: RemoteExecutionContext) -> dict: # Process all cities at once — self.config.cities is the full list. results = [await fetch_weather(city) for city in self.config.cities] return {"results": results} ``` ```mermaid theme={null} graph TD subgraph "Non-list input (auto iteration)" L1["List: A, B, C"] --> N1["Node runs 3×"] N1 --> R1["Result A"] N1 --> R2["Result B"] N1 --> R3["Result C"] end subgraph "List input (single run)" L2["List: A, B, C"] --> N2["Node runs 1×"] N2 --> R4["[Result A, B, C]"] end ``` ## Plugin-level configuration Use `PluginConfiguration` for settings that apply to the entire plugin (not per-node). These are set in **Settings → Plugins → Configure**: ```python theme={null} from noxus_sdk.plugins import PluginConfiguration from noxus_sdk.schemas import ValidationResult class WeatherPluginConfig(PluginConfiguration): default_units: str = Parameter( default="metric", display=ConfigSelect(label="Default Units", values=["metric", "imperial"]), ) cache_ttl: int = Parameter( default=300, display=ConfigNumberSlider(label="Cache TTL (seconds)", min=60, max=3600, step=60), ) def validate_config(self) -> ValidationResult: # Gate the plugin into RUNNING only when config is coherent. if self.cache_ttl < 60: return ValidationResult(valid=False, errors=["Cache TTL must be >= 60s"]) return ValidationResult(valid=True, errors=[]) ``` Access plugin config in any node — `ctx.plugin_config` is a plain dict: ```python theme={null} async def call(self, ctx: RemoteExecutionContext) -> dict: default_units = ctx.plugin_config.get("default_units", "metric") # ... ``` ## Error handling Raise the SDK's typed errors from `noxus_sdk.errors`. The message crosses the JSON-RPC sandbox boundary and becomes the node's user-visible failure reason — the exception *type* itself does not survive, so make every message actionable: ```python theme={null} from noxus_sdk.errors import IntegrationFailedError, UnexpectedError async def call(self, ctx: RemoteExecutionContext) -> dict: city = self.config.city # bindable input if not city or not city.strip(): raise IntegrationFailedError("City name cannot be empty") import httpx try: result = await fetch_weather(city) except httpx.HTTPStatusError as e: if e.response.status_code == 404: raise IntegrationFailedError(f"City '{city}' not found") from e raise UnexpectedError(f"Weather API error: {e.response.status_code}") from e return {"temperature": result["temp"], "description": result["desc"]} ``` **Best practices:** * Use `IntegrationFailedError` for credential/API/user-fixable problems. * Use `UnexpectedError` for failures you did not anticipate. * **Degrade, don't raise, for *expected* external flakiness.** A raised exception fails the whole run. For flaky network or parsing, catch it and return an empty typed result; reserve exceptions for genuine misconfiguration (e.g. no credential connected). The Proxy API scrapers do this — a failed scrape yields an empty CSV rather than crashing the flow. * Chain with `from e` and include actionable information in the message. *** ## Runtime & sandbox gotchas Your code runs as a warm worker inside a sandbox VM, reached over JSON-RPC. That environment imposes a few rules the local dev server does not: * **stdout is the protocol.** The worker speaks JSON-RPC on stdout; anything you (or a dependency) `print()` there corrupts the channel and breaks the call. Redirect noisy code to stderr: ```python theme={null} import contextlib import sys with contextlib.redirect_stdout(sys.stderr): run_chatty_library() ``` * **Import heavy deps lazily, inside `call`/helpers — not at module top.** Manifest generation and `noxus plugin validate` import your module in a lightweight environment; a top-level `import pandas` / `playwright` / `curl_cffi` makes `get_manifest()` fail with `ModuleNotFoundError` even though the sandbox has the dep. Put the import in the function that uses it. * **Degrade, don't raise, for expected external failures** (see above). * **Don't shadow a package with a same-named module.** A `weather.py` next to a `weather/` package makes `import your_pkg.weather` resolve to the package — the module becomes unreachable. Name adapters distinctly (`weather_adapter.py`). * **Zip only source.** Exclude `.venv`, `__pycache__`, `.git`, `node_modules`. An accidental `.venv` can balloon the archive from \~300 KB to tens of MB. * **Browser / JIT plugins can't run under the default `syd` sandbox.** `syd`'s seccomp filter SIGSYS-kills V8's JIT, so Playwright / Chromium / any Node or JIT-heavy workload dies on startup — even when you only drive a *remote* browser, the local Playwright Node driver still runs. Those plugins need the `gvisor` sandbox provider (Linux/EKS) or `none` (local dev — gVisor can't `pivot_root` inside Docker Desktop on macOS). This is a deployment setting (`SANDBOX_PROVIDER`), but know it before shipping a browser plugin. * **Keep `noxus-sdk>=` aligned with the platform.** The sandbox resolves `noxus-sdk` alongside your `pyproject.toml` deps; pinning an older SDK can win and the worker fails to start. Depend on `noxus-sdk` at or above the platform's version, not an old floor. *** ## Deploy your plugin ### Option 1: From a Git repository Push your plugin to a Git repository, then install from the Noxus UI: 1. Go to **Settings → Plugins → Install Plugin** 2. Choose **Git** source 3. Enter your repository URL, branch, and path (if the plugin is in a subdirectory) 4. For private repos, provide an access token ### Option 2: Upload directly Package and upload: ```bash theme={null} noxus plugin package --path ./weather-plugin --output weather-plugin.tar.gz ``` Then upload the `.tar.gz` file through the Noxus UI. ### Option 3: Marketplace Publish to the [Noxus plugins marketplace](https://github.com/Noxus-AI/noxus-plugins) for public distribution. ### Verify installation Once installed, you should see: * Your plugin listed with status **Running** in Settings → Plugins * Your nodes available in the flow editor palette * Your integration available in workspace control for credential configuration *** ## Complete example Here's the full `weather_plugin/__init__.py` putting everything together: ```python theme={null} from typing import ClassVar from noxus_sdk.errors import IntegrationFailedError from noxus_sdk.integrations.base import BaseIntegration, BaseCredentials from noxus_sdk.ncl import ConfigPassword, ConfigSelect, ConfigText, ConfigToggle, Parameter from noxus_sdk.nodes.base import BaseNodeV2, NodeConfiguration, NodeOutputs from noxus_sdk.nodes.types import NodeCategory from noxus_sdk.plugins import BasePlugin, PluginConfiguration from noxus_sdk.plugins.context import RemoteExecutionContext from noxus_sdk.plugins.types import PluginCategory # ── Integration ────────────────────────────────────────────────────── class WeatherAPICredentials(BaseCredentials): type: ClassVar[str] = "weather_api" api_key: str = Parameter( default="", description="OpenWeatherMap API key", display=ConfigPassword(label="API Key"), ) def is_ready(self) -> bool: return bool(self.api_key) class WeatherAPIIntegration(BaseIntegration[WeatherAPICredentials]): display_name = "Weather API" image = "https://cdn-icons-png.flaticon.com/512/1779/1779940.png" # ── Node schemas (V2) ──────────────────────────────────────────────── class GetWeatherConfig(NodeConfiguration): # bindable=True → a node input; read from self.config in call(). city: str = Parameter( default="", bindable=True, display=ConfigText(label="City", placeholder="e.g. London"), ) units: str = Parameter( default="metric", display=ConfigSelect(label="Units", values=["metric", "imperial"]), ) include_humidity: bool = Parameter( default=False, display=ConfigToggle(label="Include Humidity"), ) class GetWeatherOutputs(NodeOutputs): temperature: str description: str # ── Node ───────────────────────────────────────────────────────────── class GetWeatherNode(BaseNodeV2[GetWeatherConfig, GetWeatherOutputs]): node_name = "GetWeatherNode" title = "Get Weather" description = "Fetches current weather for a city" category = NodeCategory.DATA color = "#4A90E2" integrations = {"weather_api": ["api_key"]} async def call(self, ctx: RemoteExecutionContext) -> dict: import httpx creds = ctx.get_integration_credentials("weather_api") or {} api_key = creds.get("api_key", "") if not api_key: raise IntegrationFailedError("Weather API key not configured") async with httpx.AsyncClient() as client: response = await client.get( "https://api.openweathermap.org/data/2.5/weather", params={"q": self.config.city, "appid": api_key, "units": self.config.units}, ) response.raise_for_status() data = response.json() result = { "temperature": f"{data['main']['temp']}°{'C' if self.config.units == 'metric' else 'F'}", "description": data["weather"][0]["description"].capitalize(), } if self.config.include_humidity: result["description"] += f" (Humidity: {data['main']['humidity']}%)" return result # ── Plugin ─────────────────────────────────────────────────────────── class WeatherPluginConfig(PluginConfiguration): pass class WeatherPlugin(BasePlugin[WeatherPluginConfig]): name = "weather-plugin" display_name = "Weather Plugin" version = "0.1.0" description = "Weather data nodes with OpenWeatherMap integration" category = PluginCategory.GENERAL author = "Your Name" def nodes(self): return [GetWeatherNode] def integrations(self): return [WeatherAPIIntegration] ``` Browse more code examples Understand how plugins run under the hood # Data Sources Source: https://docs.noxus.ai/developers/plugins/tutorial/data-sources Feed a knowledge base from an external service by returning files to ingest A **data source** lets a plugin pull documents from an external service into a Noxus knowledge base. You implement one method — `fetch` — that gathers the documents and returns them as files; the platform ingests them. ## The class Subclass `BaseDataSource[Config]` and implement `fetch`: ```python theme={null} from noxus_sdk.datasources import BaseDataSource, DatasourceConfiguration from noxus_sdk.ncl import Parameter, ConfigText from noxus_sdk.files import File from noxus_sdk.plugins.context import RemoteExecutionContext class DriveFolderConfig(DatasourceConfiguration): folder_id: str = Parameter( default="", description="Folder to ingest", display=ConfigText(label="Folder ID"), ) class DriveFolder(BaseDataSource[DriveFolderConfig]): datasource_name = "DriveFolder" # stable id, unique within the plugin title = "Drive folder" description = "Ingest every document in a Drive folder" integrations = ["gdrive"] # credentials this source needs async def fetch(self, ctx: RemoteExecutionContext) -> list[File]: client = _client_from_ctx(ctx) helper = ctx.get_file_helper() files: list[File] = [] for doc in await client.list_folder(self.config.folder_id): content = await client.download(doc["id"]) # bytes file = await helper.upload_file( content, name=doc["name"], content_type=doc["mime"] ) files.append(file) return files ``` Register it from the plugin: ```python theme={null} def datasources(self) -> list[type[BaseDataSource]]: return [DriveFolder] ``` ## How `fetch` works Read whatever you need from the external service, using `self.config` and the integration credentials. Persist content with `ctx.get_file_helper().upload_file(bytes, name=..., content_type=...)`. The upload goes over the host callback — the bytes are **not** returned inline — and you get back a `File` descriptor. Return the `list[File]`. The platform ingests them into the knowledge base. Ingestion is **one-shot** today: `fetch` returns the current set of documents each time it runs. Incremental sync (only new/changed documents) is a later phase — `supports_sync` defaults to `False`. ## Reading credentials List the integration types in `integrations` and read them from the context: ```python theme={null} creds = ctx.get_integration_credentials("gdrive") or {} token = creds.get("access_token", "") ``` See [Creating integrations](/developers/plugins/creating-integrations) for the credential model, and [Working with files](/developers/plugins/tutorial/working-with-files) for the `File` helpers. ## Definition fields | Field | Required | Notes | | ----------------------- | -------- | --------------------------------------------------------------------- | | `datasource_name` | yes | Stable id, unique within the plugin. | | `title` / `description` | no | Shown in the KB source picker; `title` defaults to `datasource_name`. | | `integrations` | no | Credential types the source reads (`[]` if none). | | `supports_sync` | no | Incremental sync — `False` for now (one-shot fetch). | | `image` | no | Icon URL. | # 3. First Integration Source: https://docs.noxus.ai/developers/plugins/tutorial/first-integration Define credentials and an integration for an external API This is **part 3** of the [Your First Plugin](/developers/plugins/your-first-plugin) tutorial. Make sure you've completed [2. First Node](/developers/plugins/tutorial/first-node) first. Integrations handle authentication with external services. When your node needs to talk to an API that requires credentials, you define an integration so users can configure their keys securely in the Noxus UI. ## Define credentials and integration Add to `weather_plugin/__init__.py`: ```python theme={null} from typing import ClassVar from noxus_sdk.integrations.base import BaseIntegration, BaseCredentials from noxus_sdk.ncl import ConfigPassword, Parameter class WeatherAPICredentials(BaseCredentials): type: ClassVar[str] = "weather_api" api_key: str = Parameter( default="", description="Your weather API key", display=ConfigPassword(label="API Key", placeholder="Your OpenWeatherMap key"), ) def is_ready(self) -> bool: return bool(self.api_key) class WeatherAPIIntegration(BaseIntegration[WeatherAPICredentials]): display_name = "Weather API" image = "https://cdn-icons-png.flaticon.com/512/1779/1779940.png" ``` ### How it works * **`BaseCredentials`** is a Pydantic model that defines the fields users fill in (API keys, tokens, URLs, etc.). It carries a `type: ClassVar[str]` — the stable identifier used throughout the platform. * **`is_ready()`** returns `True` when the credentials are valid enough to use — the UI shows this status. * **`BaseIntegration[WeatherAPICredentials]`** ties the credentials to a display name and icon that appear in workspace control. You do **not** set `type` on the integration — the SDK derives it from the credentials class's `type` automatically (`BaseIntegration.__init_subclass__`), so the two can never drift apart. Every NCL display widget needs a **`label`** (`ConfigPassword(label="API Key")`). It has no default — a bare `ConfigText()` fails validation. Use **`ConfigPassword`** for secrets so the value is masked in the UI (see ClipOne / Proxy API plugins). ## Register the integration Update the plugin class: ```python theme={null} class WeatherPlugin(BasePlugin[WeatherPluginConfig]): # ... same metadata ... def nodes(self): return [GetWeatherNode] def integrations(self): return [WeatherAPIIntegration] ``` When the plugin is installed, this integration will appear in **Workspace control → Integrations** where users can enter their API key. Credentials are **encrypted** and stored by the platform — your plugin code never stores them. ## Integration anatomy | Component | Purpose | | ------------------- | ----------------------------------------------------------------------------------------------------------------------------- | | `type` | Unique identifier — declared on the **credentials** class as `type: ClassVar[str]`; the integration inherits it automatically | | `display_name` | Human-readable name shown in the UI | | `image` | Icon URL for the integration tile | | `credentials_class` | Pydantic model (auto-set from the `BaseIntegration[...]` generic parameter) | | `is_ready()` | Validation — returns `True` when credentials are usable | | `scopes` | Optional list of permission scopes (for OAuth-style integrations) | | `properties` | Optional dict of extra metadata | ### Probing readiness against the live API `BaseCredentials.is_ready()` only checks that fields are filled in. To make the "connected" status reflect real authorization, override the integration's `is_ready` **classmethod** and probe the API — this is what the ClipOne plugin does: ```python theme={null} class WeatherAPIIntegration(BaseIntegration[WeatherAPICredentials]): display_name = "Weather API" image = "https://cdn-icons-png.flaticon.com/512/1779/1779940.png" @classmethod async def is_ready(cls, creds: dict | None) -> bool: parsed = cls.get_credentials(creds) if parsed is None or not parsed.is_ready(): return False # Import heavy/network deps lazily — not at module top (see the runtime # gotchas in Advanced Techniques). import httpx try: async with httpx.AsyncClient() as client: r = await client.get( "https://api.openweathermap.org/data/2.5/weather", params={"q": "London", "appid": parsed.api_key}, ) return r.status_code == 200 except Exception: return False ``` ## Credential field types You can use any NCL display type for credential fields — every widget takes a `label`, and secrets should use `ConfigPassword` so they render masked: ```python theme={null} from noxus_sdk.ncl import ConfigPassword, ConfigSelect, ConfigText, ConfigToggle, Parameter class MyCredentials(BaseCredentials): type: ClassVar[str] = "my_service" # Masked secret input (use ConfigPassword for keys/tokens) api_key: str = Parameter( default="", display=ConfigPassword(label="API Key"), ) # Plain text (e.g. a base URL) base_url: str = Parameter( default="", display=ConfigText(label="Base URL", placeholder="https://api.example.com"), ) # Dropdown select region: str = Parameter( default="us-east-1", display=ConfigSelect(label="Region", values=["us-east-1", "eu-west-1", "ap-southeast-1"]), ) # Toggle use_sandbox: bool = Parameter( default=False, display=ConfigToggle(label="Use Sandbox"), ) ``` Wire the integration into your node to call a real API with credentials. # 2. First Node Source: https://docs.noxus.ai/developers/plugins/tutorial/first-node Build a node with bindable inputs, typed outputs, and logic — then test it locally This is **part 2** of the [Your First Plugin](/developers/plugins/your-first-plugin) tutorial. Make sure you've completed [1. Plugin Definition](/developers/plugins/tutorial/plugin-definition) first. Nodes are where your plugin's logic lives. Let's build a node that takes a city name and returns a formatted weather description. ## V2 vs V1 nodes There are two kinds of node, and you should almost always write **V2**: * **V2 (recommended, connector-free).** A V2 node mirrors the platform's native nodes: it has **no edge connectors**. Instead it is two schemas — a **config schema** whose fields are the node's settings (a field marked `bindable=True` becomes an **input** that accepts a literal or a `:var[...]` reference to an upstream output), and an **output schema** that declares every output. Everything arrives on `self.config`; `call(ctx)` returns a dict keyed by the output field names. * **V1 (legacy — edge connectors).** Inputs and outputs are `Connector` objects wired by edges, and inputs arrive as keyword arguments to `call`. Only use V1 when you specifically need a node for a V1 flow. Both kinds can live in the same plugin — `nodes()` returns them together and the SDK splits them into the correct editor automatically. ## Define the node Add a new node to `weather_plugin/`: ```python theme={null} from noxus_sdk.nodes.base import BaseNodeV2, NodeConfiguration, NodeOutputs from noxus_sdk.nodes.types import NodeCategory from noxus_sdk.ncl import Parameter, ConfigText, ConfigSelect from noxus_sdk.plugins.context import RemoteExecutionContext class GetWeatherConfig(NodeConfiguration): # `bindable=True` makes this a node input: it accepts a literal typed in the # editor, or a `:var[...]` reference to an upstream node's output. city: str = Parameter( default="", bindable=True, display=ConfigText(label="City", placeholder="e.g. London"), ) # A plain (non-bindable) config field — a setting the user picks in the editor. units: str = Parameter( default="celsius", display=ConfigSelect(label="Units", values=["celsius", "fahrenheit"]), ) class GetWeatherOutputs(NodeOutputs): temperature: str description: str class GetWeatherNode(BaseNodeV2[GetWeatherConfig, GetWeatherOutputs]): node_name = "GetWeatherNode" title = "Get Weather" description = "Returns weather data for a given city" category = NodeCategory.DATA color = "#4A90E2" async def call(self, ctx: RemoteExecutionContext) -> dict: # Every value — bindable inputs included — is read from self.config. temperature = "22°C" if self.config.units == "celsius" else "72°F" return { "temperature": temperature, "description": f"Sunny skies in {self.config.city}", } ``` A `list[X]`-typed output field (e.g. `tags: list[str]`) becomes a list output. Required-but-unbound inputs fail with a clear error before `call` runs, so you don't need to defensively check them. Only reach for V1 when you need a node for a V1 flow. Inputs and outputs are `Connector` objects, and `call` receives inputs as keyword arguments: ```python theme={null} from noxus_sdk.nodes.base import BaseNode, NodeConfiguration from noxus_sdk.nodes.connector import Connector from noxus_sdk.nodes.types import DataType, NodeCategory, TypeDefinition from noxus_sdk.plugins.context import RemoteExecutionContext class GetWeatherConfig(NodeConfiguration): """This node has no configuration fields.""" pass class GetWeatherNode(BaseNode[GetWeatherConfig]): node_name = "GetWeatherNode" title = "Get Weather" description = "Returns weather data for a given city" category = NodeCategory.DATA color = "#4A90E2" inputs = [ Connector( name="city", label="City", definition=TypeDefinition(data_type=DataType.str), ), ] outputs = [ Connector( name="temperature", label="Temperature", definition=TypeDefinition(data_type=DataType.str), ), Connector( name="description", label="Description", definition=TypeDefinition(data_type=DataType.str), ), ] async def call(self, ctx: RemoteExecutionContext, city: str) -> dict: return { "temperature": "22°C", "description": f"Sunny skies in {city}", } ``` ## Register the node in your plugin Add the node class to your plugin's `nodes()` method: ```python theme={null} class WeatherPlugin(BasePlugin[WeatherPluginConfig]): # ... same metadata as before ... def nodes(self): return [GetWeatherNode] ``` ## Test it locally Restart the plugin server: ```bash theme={null} noxus plugin serve --path ./weather-plugin ``` Then execute the node. The `/nodes/{node_name}/execute` endpoint takes a body with `ctx`, `inputs`, and `config`: For a V2 node, bindable and plain config values both go in `config`; `inputs` stays empty: ```bash theme={null} curl -X POST http://localhost:8505/nodes/GetWeatherNode/execute \ -H "Content-Type: application/json" \ -d '{ "ctx": {}, "inputs": {}, "config": {"city": "London", "units": "celsius"} }' ``` For a V1 node, connector inputs go in `inputs` as plain values keyed by name: ```bash theme={null} curl -X POST http://localhost:8505/nodes/GetWeatherNode/execute \ -H "Content-Type: application/json" \ -d '{ "ctx": {}, "inputs": {"city": "London"}, "config": {} }' ``` Either way you get back an `ExecutionResponse`: ```json theme={null} { "success": true, "outputs": { "temperature": "22°C", "description": "Sunny skies in London" } } ``` ## Key concepts | Concept | Description | | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | | `node_name` | Unique identifier — must be unique across all plugins | | Config schema (V2) | A `NodeConfiguration`; `bindable=True` fields are inputs, the rest are settings | | Output schema (V2) | A `NodeOutputs` subclass; each field is an output, `list[X]` is a list output | | `inputs` / `outputs` (V1) | `Connector` objects that define what flows in and out over edges | | `call()` | The async method that runs when the node executes. V2 reads `self.config` and returns a dict keyed by output names; V1 receives inputs as kwargs | | `ctx` | The `RemoteExecutionContext` — group id, integration credentials, and file helpers | ### Available data types V2 output fields and V1 connectors both use these types (V2 infers them from the Python annotation; V1 declares them via `TypeDefinition`): | Python annotation (V2) | `DataType` (V1) | Meaning | | ---------------------- | ------------------------------------------------------- | --------------- | | `str` | `DataType.str` | Text | | `int` / `float` | `DataType.number` | Number | | `bool` | `DataType.bool` | Boolean | | `dict` | `DataType.dict` | JSON / dict | | `datetime` | `DataType.datetime` | Timestamp | | `File` | `DataType.File` | Any file | | `list[str]` | `TypeDefinition(data_type=DataType.str, is_list=True)` | List of strings | | `list[File]` | `TypeDefinition(data_type=DataType.File, is_list=True)` | List of files | `Image`, `Audio`, and `Chat` are also available as file-like types. ### Node metadata | Field | Required | Description | | ------------------- | -------- | -------------------------------------------------------------------------------------------------------- | | `node_name` | Yes | Unique identifier for the node | | `title` | Yes | Display name in the UI | | `description` | Yes | Shown in the node palette and tooltips | | `category` | No | Groups the node in the palette (`DATA`, `AI_TEXT`, `INTEGRATIONS`, `LOGIC`, `OTHER`, …); default `OTHER` | | `color` | No | Hex color for the node in the editor | | `image` | No | Icon URL (PNG/SVG) | | `small_description` | No | Short one-line summary | | `sub_category` | No | Finer grouping within a category | | `integrations` | No | Dict mapping integration types to required credential fields | | `max_timeout` | No | Maximum execution time in seconds (default `240`) | Define credentials and an integration for an external API. # 1. Plugin Definition Source: https://docs.noxus.ai/developers/plugins/tutorial/plugin-definition Scaffold the project, define the plugin class, validate, and run locally This is **part 1** of the [Your First Plugin](/developers/plugins/your-first-plugin) tutorial. A plugin is an installable Python package that adds **nodes**, **integrations**, **triggers**, and **datasources** to the platform. At install time the platform reads the plugin's **manifest** (derived from your code — never hand-written), provisions a sandbox, and runs your plugin as a warm worker. The plugin class is the entry point that ties everything together. ## Scaffold the project Use the Noxus CLI to generate a plugin from the template: ```bash theme={null} pip install noxus-sdk noxus plugin create --output-dir ./my-plugins ``` `create` is interactive — it prompts for a few values and expands a template: ``` plugin_name [my-plugin]: weather-plugin description [A new Noxus plugin]: Provides weather data nodes author_name [Your Name]: Your Name Select include_integration [yes]: no ``` This produces the following structure (package names are derived from `plugin_name`): ``` weather-plugin/ ├── weather_plugin/ │ ├── __init__.py # the plugin class lives here │ └── nodes/ │ ├── __init__.py │ └── weather_plugin_node.py # an example node ├── tests/ │ └── __init__.py ├── pyproject.toml └── README.md ``` There is **no `manifest.json`** in the scaffold. The manifest is generated from your code — see [Generate the manifest](#generate-the-manifest) below. Never write or edit it by hand. Prefer to start from scratch? A plugin only needs a package with your plugin class plus a `pyproject.toml`. Here's a minimal `pyproject.toml`: ```toml theme={null} [build-system] requires = ["hatchling"] build-backend = "hatchling.build" [project] name = "weather-plugin" version = "0.1.0" description = "A weather data plugin for Noxus" requires-python = ">=3.11" dependencies = [ "noxus-sdk>=0.5.1", "httpx>=0.27.0", ] ``` Keep `noxus-sdk` at or above the platform's version. The sandbox resolves `noxus-sdk` alongside your dependencies; pinning an older floor can make the worker fail to start. ## Define the plugin class Open `weather_plugin/__init__.py`. A plugin subclasses `BasePlugin[YourConfig]` and returns the components it provides from `nodes()`, `integrations()`, `triggers()`, and `datasources()`: ```python theme={null} from noxus_sdk.plugins import BasePlugin, PluginConfiguration from noxus_sdk.plugins.types import PluginCategory class WeatherPluginConfig(PluginConfiguration): """Plugin-level configuration — empty for now.""" pass class WeatherPlugin(BasePlugin[WeatherPluginConfig]): name = "weather-plugin" display_name = "Weather Plugin" version = "0.1.0" description = "Provides weather data nodes and integrations" category = PluginCategory.GENERAL author = "Your Name" def nodes(self): return [] # We'll add nodes in the next section def integrations(self): return [] # We'll add integrations later ``` Every plugin defines this metadata: | Field | Required | Description | | -------------- | -------- | --------------------------------------------------------------------- | | `name` | Yes | Unique identifier used for lookups, dependencies, and storage | | `display_name` | Yes | Human-readable name shown in the UI | | `version` | Yes | Semantic version string (e.g. `"0.1.0"`) | | `description` | Yes | Short description of what the plugin does | | `author` | Yes | Author name | | `category` | No | `PluginCategory.GENERAL`, `.DOCUMENT`, or `.OTHER` (default `.OTHER`) | | `execution` | No | `"runtime"` (default), `"docker"`, or `"remote"` | The four provider methods return lists of classes your plugin exposes. A plugin **must provide at least one** node, integration, trigger, or datasource: | Method | Returns | | ---------------- | ------------------------------------------------------------ | | `nodes()` | `list[type[BaseNode]]` — V1 and V2 nodes may be mixed freely | | `integrations()` | `list[type[BaseIntegration]]` | | `triggers()` | `list[type[BasePollingTrigger]]` | | `datasources()` | `list[type[BaseDataSource]]` | We'll populate `nodes()` in the next section. ## Validate the structure ```bash theme={null} noxus plugin validate --path ./weather-plugin ``` `validate` imports your plugin class, generates the manifest in memory, and reports any errors or warnings. Run it often as you develop. Add `--strict` to make warnings fail too. ## Generate the manifest The manifest is the install-time contract, derived entirely from your code. Generate `manifest.json` with: ```bash theme={null} noxus plugin generate-manifest --path ./weather-plugin ``` Regenerate it whenever a node, trigger, integration, or config signature changes. When you're ready to ship, `noxus plugin package --path ./weather-plugin` bundles the source and a fresh manifest into an archive you upload via the platform's **Add plugin** flow. ## Run locally For a quick local authoring loop, serve the plugin over HTTP: ```bash theme={null} noxus plugin serve --path ./weather-plugin ``` You'll see output like: ``` PLUGIN_PORT:8505 INFO: Started server process INFO: Waiting for application startup INFO: Application startup complete ``` Visit `http://localhost:8505/health` to verify it's running, or `http://localhost:8505/manifest` to inspect the generated manifest. `serve` is a local authoring aid. On the platform, plugins don't run as an HTTP server — they run as sandboxed JSON-RPC workers, and file operations (reading or writing platform files) are only available there, not under `serve`. Use `serve` to iterate on node logic that doesn't touch platform files; use the install flow to exercise the full runtime. The plugin won't do much yet — let's add a node. Create your first node with bindable inputs, typed outputs, and logic. # Polling Triggers Source: https://docs.noxus.ai/developers/plugins/tutorial/polling-triggers Start a workflow run for each new event a plugin discovers by polling an external service A **polling trigger** starts a workflow run whenever something new happens in an external service. The platform calls your trigger on a fixed interval; you check the service, return the new events, and hand back a bit of state so the next poll knows where it left off. Each event you return becomes the **inputs** of a triggered workflow run. ## The class Subclass `BasePollingTrigger[Config]` and implement `poll`: ```python theme={null} from noxus_sdk.triggers import BasePollingTrigger, TriggerConfiguration from noxus_sdk.ncl import Parameter, ConfigText from noxus_sdk.plugins.context import RemoteExecutionContext class NewTicketConfig(TriggerConfiguration): queue: str = Parameter( default="", description="Queue to watch", display=ConfigText(label="Queue name"), ) class NewTicket(BasePollingTrigger[NewTicketConfig]): trigger_name = "NewTicket" # stable id, unique within the plugin title = "New ticket" description = "Runs once per new ticket in a queue" integrations = ["helpdesk"] # credentials this trigger needs polling_interval = 60.0 # seconds between polls outputs = {"ticket_id": "str"} # event fields → type labels async def poll( self, ctx: RemoteExecutionContext, state: dict ) -> tuple[list[dict], dict]: seen: list[str] = list(state.get("seen", [])) client = _client_from_ctx(ctx) tickets = await client.list_tickets(self.config.queue) events: list[dict] = [] for ticket in tickets: ticket_id = str(ticket["id"]) if ticket_id in seen: continue events.append({"ticket_id": ticket_id}) # keys must match `outputs` seen.append(ticket_id) return events, {"seen": seen} ``` Register it from the plugin: ```python theme={null} def triggers(self) -> list[type[BasePollingTrigger]]: return [NewTicket] ``` ## How `poll` works The platform invokes `poll(ctx, state)` every `polling_interval` seconds. `state` is whatever your previous poll returned (an empty `dict` on the first call). Return `(events, new_state)`. Each event is a JSON-serializable `dict` whose keys **match your `outputs`** — those fields become the triggered run's inputs. Put a watermark (last-seen id/timestamp, or a set of processed ids) in `new_state` so the next poll only returns genuinely new events. The platform stores it for you between polls. Return an **empty list** when nothing is new — that is the normal case, and it starts no runs. Only return an event the first time you see it; deduping via `state` is what keeps a run from firing twice for the same item. ## Reading credentials List the integration types the trigger needs in `integrations`, then read them from the context the same way a node does: ```python theme={null} creds = ctx.get_integration_credentials("helpdesk") or {} api_key = creds.get("api_key", "") ``` See [Creating integrations](/developers/plugins/creating-integrations) for the credential model. ## Errors Raise `IntegrationFailedError` (from `noxus_sdk.errors`) when the external call fails — only the message crosses the sandbox boundary, so make it actionable. Don't let an exception escape for an *expected* "nothing new" result; return an empty list instead. ```python theme={null} from noxus_sdk.errors import IntegrationFailedError resp = await client.list_tickets(self.config.queue) if resp.status_code != 200: raise IntegrationFailedError(f"Failed to list tickets: {resp.status_code}") ``` ## Definition fields | Field | Required | Notes | | ----------------------- | -------- | ----------------------------------------------------------------------------- | | `trigger_name` | yes | Stable id, unique within the plugin. | | `title` / `description` | no | Shown in the editor; `title` defaults to `trigger_name`. | | `integrations` | no | Credential types the trigger reads (`[]` if none). | | `polling_interval` | no | Seconds between polls (default `300`). | | `outputs` | no | `{field: "type label"}` — the shape of each event / the trigger's run inputs. | | `image` | no | Icon URL. | # 4. Use Integration in Node Source: https://docs.noxus.ai/developers/plugins/tutorial/using-integrations Wire the integration into your node to call a real API with credentials This is **part 4** of the [Your First Plugin](/developers/plugins/your-first-plugin) tutorial. Make sure you've completed [3. First Integration](/developers/plugins/tutorial/first-integration) first. Now let's connect the node to the integration so it uses real credentials to call an external API. ## Declare the integration dependency Update the node class to reference the integration: ```python theme={null} class GetWeatherNode(BaseNodeV2[GetWeatherConfig, GetWeatherOutputs]): node_name = "GetWeatherNode" title = "Get Weather" description = "Returns weather data for a given city" category = NodeCategory.DATA color = "#4A90E2" integrations = {"weather_api": ["api_key"]} # integration_type: [required_fields] # ... same config schema (with a bindable `city`) and output schema ... async def call(self, ctx: RemoteExecutionContext) -> dict: # Lazy-import network deps inside call() — a top-level `import httpx` # can break manifest generation (see the runtime gotchas in Advanced # Techniques). import httpx from noxus_sdk.errors import IntegrationFailedError # Get credentials from the execution context creds = ctx.get_integration_credentials("weather_api") or {} api_key = creds.get("api_key", "") if not api_key: raise IntegrationFailedError( "No Weather API key connected — add it in Workspace Settings → Integrations." ) # Bindable inputs are read from self.config, just like any config field. city = self.config.city # Call the real API async with httpx.AsyncClient() as client: response = await client.get( "https://api.openweathermap.org/data/2.5/weather", params={"q": city, "appid": api_key, "units": "metric"}, ) response.raise_for_status() data = response.json() return { "temperature": f"{data['main']['temp']}°C", "description": data["weather"][0]["description"].capitalize(), } ``` `integrations` works identically on V1 and V2 nodes. This continues the V2 `GetWeatherNode` from [part 2](/developers/plugins/tutorial/first-node) — its `city` input is a `bindable=True` config field, so `call(ctx)` reads `self.config.city` rather than taking a `city` argument. (A V1 node would instead receive `city` as a keyword argument.) ### What changed 1. **`integrations = {"weather_api": ["api_key"]}`** — declares this node depends on the `weather_api` integration and needs the `api_key` field. The platform shows a credential picker in the editor and injects the decrypted credentials into `ctx` for the run. 2. **`ctx.get_integration_credentials("weather_api")`** — retrieves the decrypted credentials at runtime as a plain `dict` keyed by your `BaseCredentials` field names. Fall back with `or {}` and read fields with `.get(...)` so a missing credential is a clean, actionable failure. 3. **`raise IntegrationFailedError(...)`** — the SDK's typed error for credential/API problems (`noxus_sdk.errors`). Prefer it over a bare `ValueError`: the message crosses the sandbox boundary and becomes the node's user-visible failure reason (the exception *type* itself does not cross, so make the message actionable). ## How credentials flow ```mermaid theme={null} sequenceDiagram participant U as User participant WC as Workspace Control participant W as Worker participant SM as Sandbox Manager participant P as Plugin Worker U->>WC: Enter API key for Weather API WC->>WC: Encrypt & store credentials Note over W: Workflow runs... W->>SM: Execute get_weather node Note over W: Includes decrypted credentials in ctx SM->>P: Forward with ctx.integration_credentials P->>P: ctx.get_integration_credentials("weather_api") P->>P: Call external API with api_key ``` Credentials are **never stored in the plugin**. They're decrypted by the platform at execution time and passed through the `RemoteExecutionContext` for that specific run. ## Using multiple integrations A node can depend on more than one integration: ```python theme={null} class CrossPostConfig(NodeConfiguration): message: str = Parameter( default="", bindable=True, display=ConfigBigText(label="Message"), ) class CrossPostOutputs(NodeOutputs): status: str class CrossPostNode(BaseNodeV2[CrossPostConfig, CrossPostOutputs]): node_name = "CrossPostNode" title = "Cross Post" description = "Posts a message to multiple networks" integrations = { "twitter": ["api_key", "api_secret"], "linkedin": ["access_token"], } async def call(self, ctx: RemoteExecutionContext) -> dict: twitter_creds = ctx.get_integration_credentials("twitter") or {} linkedin_creds = ctx.get_integration_credentials("linkedin") or {} # Use both sets of credentials to post self.config.message ... return {"status": "posted"} ``` ## A reusable credentials helper Real plugins wrap credential lookup in a small helper next to the integration, so every node reads the same fields the same way. The Proxy API scraper plugin does exactly this: ```python theme={null} def api_key_from_ctx(ctx: RemoteExecutionContext) -> str: creds = ctx.get_integration_credentials("proxy_api") or {} return creds.get("api_key", "") ``` ## Error handling for credentials Always validate that credentials exist before using them, and raise `IntegrationFailedError` so the message surfaces as the node's failure reason: ```python theme={null} from noxus_sdk.errors import IntegrationFailedError async def call(self, ctx: RemoteExecutionContext) -> dict: creds = ctx.get_integration_credentials("weather_api") or {} if not creds.get("api_key"): raise IntegrationFailedError( "Weather API credentials not configured. " "Go to Workspace control → Integrations to add your API key." ) # Safe to use credentials... ``` Read and create files from plugin nodes. # 5. Working with Files Source: https://docs.noxus.ai/developers/plugins/tutorial/working-with-files Read and create files from plugin nodes This is **part 5** of the [Your First Plugin](/developers/plugins/your-first-plugin) tutorial. Make sure you've completed [4. Use Integration in Node](/developers/plugins/tutorial/using-integrations) first. Plugins can read and create files. Since plugins run in isolated processes, file I/O goes through the platform's file helper — the SDK handles all the bridging transparently. ## Reading files Add a node that reads a file input: A `File` flows through a V2 node like any other value: declare a **bindable** `File` config field to receive one, and a `File`-typed output field to emit one. ```python theme={null} from noxus_sdk.files import File from noxus_sdk.nodes.base import BaseNodeV2, NodeConfiguration, NodeOutputs from noxus_sdk.nodes.types import NodeCategory from noxus_sdk.ncl import ConfigFile, Parameter from noxus_sdk.plugins.context import RemoteExecutionContext class ParseWeatherFileConfig(NodeConfiguration): file: File = Parameter(bindable=True, display=ConfigFile(label="Cities File")) class ParseWeatherFileOutputs(NodeOutputs): cities: list[str] class ParseWeatherFileNode(BaseNodeV2[ParseWeatherFileConfig, ParseWeatherFileOutputs]): node_name = "ParseWeatherFileNode" title = "Parse Weather File" description = "Reads a CSV file of cities and returns weather data" category = NodeCategory.DATA color = "#4A90E2" async def call(self, ctx: RemoteExecutionContext) -> dict: # The bindable File input is read from self.config. file = self.config.file # Read file content through the file helper. content = await file.get_content(ctx) text = content.decode("utf-8") # Parse CSV lines. cities = [line.strip() for line in text.splitlines() if line.strip()] return {"cities": cities} ``` ### How file reading works When a `File` type input arrives, it contains metadata (name, URI, content type) but not the actual bytes. Calling `file.get_content(ctx)` triggers: ```mermaid theme={null} sequenceDiagram participant N as Node Code participant SDK as File Helper participant SM as Sandbox Manager participant P as Platform Storage N->>SDK: file.get_content(ctx) SDK->>SM: host.get_content (callback) SM->>P: Fetch file for this workspace P-->>SM: File bytes SM-->>SDK: File bytes SDK-->>N: bytes ``` Your plugin runs inside a sandbox with no direct access to platform storage, so the SDK asks the platform for the bytes over the same channel the platform used to call your node. The platform only serves files belonging to the workspace the run is executing for — a plugin cannot reach another workspace's files. You can also access file metadata without downloading: ```python theme={null} file.name # "cities.csv" file.content_type # "text/csv" file.uri # "spot://..." file.id # UUID string ``` ## Creating files ```python theme={null} from noxus_sdk.ncl import ConfigBigText, Parameter class GenerateReportConfig(NodeConfiguration): report_text: str = Parameter( default="", bindable=True, display=ConfigBigText(label="Report Text"), ) class GenerateReportOutputs(NodeOutputs): report_file: File class GenerateReportNode(BaseNodeV2[GenerateReportConfig, GenerateReportOutputs]): node_name = "GenerateReportNode" title = "Generate Weather Report" description = "Creates a text file with weather data" category = NodeCategory.DATA color = "#4A90E2" async def call(self, ctx: RemoteExecutionContext) -> dict: # Create a file through the file helper. report_file = await File.from_bytes( ctx, self.config.report_text.encode("utf-8"), name="weather_report.txt", content_type="text/plain", ) return {"report_file": report_file} ``` `File.from_bytes()` uploads the content to the platform's storage and returns a `File` object that downstream nodes can use. `File.from_bytes_internal_uri()` is an alias with a self-documenting name — reach for it at explicit persistence sites (e.g. saving a downloaded attachment) when you want the call to read clearly. ## Giving a library real file paths Some libraries need a real path on disk rather than a `File` reference. Download inputs into the sandbox's local filesystem with `persist_files_locally`: ```python theme={null} from noxus_sdk.files import persist_files_locally # self.config.files is a bindable `list[File]` input on the node's config schema. async def call(self, ctx: RemoteExecutionContext) -> dict: # Writes each File's bytes under a local dir and returns the dir path. folder = await persist_files_locally(ctx, self.config.files) # ... hand `folder` to a library that wants paths, then upload results back return {"folder": folder} ``` The directory lives on the plugin's own sandbox disk (default: a fresh dir under `/tmp`); it is not platform storage. To hand results back to the flow, upload them again with `File.from_bytes`. ## Quick reference | Operation | Code | | ---------------------------------- | --------------------------------------------------------------------------------------------- | | Read file content | `content = await file.get_content(ctx)` | | Create from bytes | `await File.from_bytes(ctx, b"...", name="file.txt")` | | Create from text | `await File.from_bytes(ctx, text.encode("utf-8"), name="out.txt", content_type="text/plain")` | | Create (explicit persistence site) | `await File.from_bytes_internal_uri(ctx, data, name="attachment.pdf")` | | Download inputs to local disk | `path = await persist_files_locally(ctx, files)` | | Access file name | `file.name` | | Access content type | `file.content_type` | | Access file URI | `file.uri` | | File input (V2) | bindable `File` config field: `Parameter(bindable=True, display=ConfigFile(label=...))` | | File output (V2) | a `File`-typed field on the `NodeOutputs` schema | | File list (V2) | `list[File]` annotation (bindable input or output field) | `data`, `name`, and `content_type` are positional/keyword on `File.from_bytes(ctx, data, name=..., content_type=...)` — `data` is the first argument after `ctx`, not a keyword-only field. (On a V1 node these are declared instead with `TypeDefinition(data_type=DataType.File)` connectors.) ## Handling multiple files Use a `list[File]` field to receive or produce multiple files. On a V2 node it's a bindable `list[File]` config field (input) and/or a `list[File]` output field: ```python theme={null} class MergeFilesConfig(NodeConfiguration): files: list[File] = Parameter( default_factory=list, bindable=True, display=ConfigFileArray(label="Input Files"), ) class MergeFilesOutputs(NodeOutputs): combined_text: str class MergeFilesNode(BaseNodeV2[MergeFilesConfig, MergeFilesOutputs]): node_name = "MergeFilesNode" title = "Merge Files" description = "Concatenates the text of several files" async def call(self, ctx: RemoteExecutionContext) -> dict: all_text = [] for f in self.config.files: content = await f.get_content(ctx) all_text.append(content.decode("utf-8")) return {"combined_text": "\n---\n".join(all_text)} ``` ## Parsing email If your node receives raw email bytes, the SDK's `Email` helper parses an RFC-822 message into a typed model whose attachments and inline images are persisted to platform storage as `File`s — over the same host callbacks, so it never needs network access back to the platform: ```python theme={null} import email from noxus_sdk.email import Email from noxus_sdk.errors import IntegrationFailedError # self.config.raw is a bindable `File` input carrying the raw .eml bytes. async def call(self, ctx: RemoteExecutionContext) -> dict: data = await self.config.raw.get_content(ctx) msg = email.message_from_bytes(data, policy=email.policy.default) parsed = await Email.from_email_object(ctx, msg) if not parsed.attachments: raise IntegrationFailedError("Email had no attachments to process") # .to_text() renders subject/body for LLM or tool input; # parsed.attachments are File refs already saved to storage. return {"text": parsed.to_text(), "files": parsed.attachments} ``` Pass `zip_attachments=True` to `Email.from_email_object` to collapse all attachments into a single `attachments.zip` file instead of one `File` per part. Config UI controls, dynamic config, list handling, error handling, and deployment. # Your First Plugin Source: https://docs.noxus.ai/developers/plugins/your-first-plugin Step-by-step tutorial — from plugin definition to deployed nodes and integrations This tutorial walks you through building a complete plugin from scratch with the `noxus plugin` CLI. By the end you'll have a plugin with custom nodes, an integration, file handling, and a manifest ready to install on Noxus. A plugin is an installable Python package whose **manifest** (generated from your code, never hand-written) tells the platform what nodes, integrations, and triggers it provides. New nodes should be **V2** — connector-free, with bindable config fields as inputs and a typed output schema; V1 connector nodes are legacy. The [First Node](/developers/plugins/tutorial/first-node) section covers both. We'll build a **Weather Plugin** across six sections, each adding a new capability: ```mermaid theme={null} graph LR S1["1. Plugin
Definition"] --> S2["2. First
Node"] S2 --> S3["3. First
Integration"] S3 --> S4["4. Use Integration
in Node"] S4 --> S5["5. Working
with Files"] S5 --> S6["6. Advanced
Techniques"] ``` Scaffold the project, define the plugin class, validate, and run locally. Build a node with bindable inputs, typed outputs, and logic. Test it with curl. Define credentials and an integration for an external API. Wire the integration into your node to call a real API with credentials. Read and create files from plugin nodes. Config UI controls, dynamic config, list handling, error handling, and deployment. ## Prerequisites * Python 3.11+ * The Noxus SDK installed: `pip install noxus-sdk` * Basic familiarity with Python, Pydantic, and async/await ## What we're building A **Weather Plugin** that: * Provides a **Get Weather** node that fetches weather data for any city * Includes a **Weather API** integration to manage API key credentials securely * Has nodes for **reading** and **creating** files * Uses **configuration UI controls** so users can customize behavior in the editor Each section builds on the previous one — start at section 1 and work through them in order, or jump to the section you need. # Admin: OAuth Provider Setup Source: https://docs.noxus.ai/integrations/admin-setup How to register OAuth apps and configure providers in the Noxus Admin panel Before users can connect OAuth integrations (Google, Microsoft, Slack, Notion, etc.), an administrator must register an OAuth application with each provider and enter the credentials in the Noxus Admin panel. **Who does this?** This is a one-time setup per provider, done by a Noxus platform administrator. Once configured, all workspace users can connect their own accounts without any further admin involvement. *** ## How it works ```mermaid theme={null} sequenceDiagram participant A as Admin participant NP as Noxus Admin Panel participant P as Provider (Google / Microsoft / …) participant U as End User Note over A,P: One-time setup A->>P: Create OAuth app, copy Client ID & Secret A->>NP: Enter credentials in Admin → Integrations → Providers Note over U,P: Per-user, self-service U->>NP: Click "Connect" in Workspace Control NP->>P: OAuth consent flow P->>U: Grant access NP->>NP: Store tokens encrypted ``` *** ## Step-by-step: Admin panel Navigate to **Admin → Integrations** in the sidebar (gear icon). Switch to the **OAuth Providers** tab. Each provider shows its configuration status (green = configured, amber = missing credentials). Click **Configure** (or **Update**) on the provider card. A dialog opens showing the required fields (Client ID, Client Secret, and optionally a Tenant ID for Microsoft). Paste the credentials you obtained from the provider's developer console (see provider-specific guides below). Click **Save**. The provider status changes to green. Users can now connect their accounts. *** ## Provider-specific setup guides ### 1 — Create a Google Cloud project 1. Go to [console.cloud.google.com](https://console.cloud.google.com) and create a new project (or use an existing one). ### 2 — Enable APIs Enable the APIs you need: | Service | API to enable | | :-------------- | :------------------ | | Google Drive | Google Drive API | | Gmail | Gmail API | | Google Sheets | Google Sheets API | | Google Calendar | Google Calendar API | Navigate to **APIs & Services → Library** and search for each API. ### 3 — Configure the OAuth consent screen 1. Go to **APIs & Services → OAuth consent screen**. 2. Select **External** (or Internal if restricting to your Google Workspace org). 3. Fill in App name, support email, and developer email. 4. Add **Authorized domains**: your Noxus instance domain (e.g. `noxus.yourcompany.com`). 5. Add the required **Scopes** (you can add scopes or leave them blank — Noxus requests scopes dynamically per integration). 6. Save and continue. ### 4 — Create OAuth 2.0 credentials 1. Go to **APIs & Services → Credentials → Create Credentials → OAuth client ID**. 2. Application type: **Web application**. 3. Name: anything (e.g. "Noxus Production"). 4. **Authorised redirect URIs** — add exactly: ``` https:///api/backend/integrations/oauth/callback ``` 5. Click **Create**. Copy the **Client ID** and **Client Secret**. ### 5 — Enter in Noxus Admin In **Admin → Integrations → OAuth Providers → Google Workspace**, enter: * **Client ID** — the value from step 4 * **Client Secret** — the value from step 4 ### Option A — OAuth (User-Delegated) User connects their own Microsoft account — actions appear as coming from that user. #### 1 — Register an app in Azure Entra ID 1. Go to [portal.azure.com](https://portal.azure.com) → **Azure Active Directory → App registrations → New registration**. 2. Name: anything (e.g. "Noxus OAuth"). 3. Supported account types: **Accounts in any organizational directory and personal Microsoft accounts** (or restrict to your tenant if needed). 4. Redirect URI: `Web` → `https:///api/backend/integrations/oauth/callback` 5. Click **Register**. #### 2 — Add API permissions Go to **API permissions → Add a permission → Microsoft Graph → Delegated permissions**: | Service | Permissions | | :----------- | :----------------------------------------- | | Outlook mail | `Mail.Read`, `Mail.Send`, `Mail.ReadWrite` | | Calendar | `Calendars.Read`, `Calendars.ReadWrite` | | OneDrive | `Files.Read.All`, `Files.ReadWrite.All` | | SharePoint | `Sites.Read.All`, `Sites.ReadWrite.All` | | Teams | `ChannelMessage.Read.All`, `Chat.Read` | Click **Grant admin consent for \[your tenant]**. #### 3 — Create a client secret Go to **Certificates & secrets → New client secret**. Copy the **Value** (not the ID). #### 4 — Enter in Noxus Admin In **Admin → Integrations → OAuth Providers → Microsoft 365 (OAuth)**, enter: * **Client ID** — Application (client) ID from the Overview page * **Client Secret** — the secret value from step 3 * **Tenant ID** *(optional)* — leave blank for multi-tenant, or enter your Directory (tenant) ID to restrict to your org *** ### Option B — Service Principal (Application Permissions) Application acts with its own identity — no user sign-in required. Suitable for background workflows that need broad org-wide access. Service Principal requires **admin consent** from a Microsoft Global Admin and grants broader access than OAuth. Use OAuth unless you specifically need app-level permissions. #### 1 — Register a separate app in Azure Entra ID Follow steps 1–3 above, but select **Application permissions** instead of Delegated. Required permissions for Service Principal: | Service | Permissions | | :------ | :----------------------------------------- | | Mail | `Mail.Read`, `Mail.Send`, `Mail.ReadWrite` | | Files | `Files.Read.All`, `Files.ReadWrite.All` | | Sites | `Sites.Read.All`, `Sites.ReadWrite.All` | | Teams | `ChannelMessage.Read.All` | #### 2 — Enter in Noxus Admin In **Admin → Integrations → OAuth Providers → Microsoft 365 (Service Principal)**, enter: * **Tenant ID** — your Directory (tenant) ID * **Client ID** — Application (client) ID * **Client Secret** — the secret value ### 1 — Create a Slack app 1. Go to [api.slack.com/apps](https://api.slack.com/apps) → **Create New App → From scratch**. 2. Name your app (e.g. "Noxus") and pick your workspace for development. ### 2 — Configure OAuth 1. In the left sidebar, go to **OAuth & Permissions**. 2. Under **Redirect URLs**, add: ``` https:///api/backend/integrations/oauth/callback ``` 3. Under **Bot Token Scopes**, add: * `channels:read`, `channels:history` * `chat:write` * `users:read` * `files:read`, `files:write` ### 3 — Get credentials Go to **Basic Information** → **App Credentials**. Copy: * **Client ID** * **Client Secret** ### 4 — Enter in Noxus Admin In **Admin → Integrations → OAuth Providers → Slack**, enter the Client ID and Client Secret. ### 1 — Create a Notion integration 1. Go to [www.notion.com/my-integrations](https://www.notion.com/my-integrations) → **New integration**. 2. Name: anything (e.g. "Noxus"). 3. Select the workspace. 4. Under **Capabilities**, choose **Read content**, **Update content**, **Insert content**. 5. Under **Distribution**, switch to **Public** (required for OAuth). ### 2 — Configure OAuth In the integration settings, under **OAuth Domain & URIs**: * **Redirect URIs**: `https:///api/backend/integrations/oauth/callback` ### 3 — Get credentials Under **OAuth Credentials**, copy: * **OAuth client ID** * **OAuth client secret** ### 4 — Enter in Noxus Admin In **Admin → Integrations → OAuth Providers → Notion**, enter the OAuth client ID and client secret. ### 1 — Create a GitHub OAuth App 1. Go to **GitHub → Settings → Developer settings → OAuth Apps → New OAuth App**. 2. Application name: anything (e.g. "Noxus"). 3. Homepage URL: your Noxus instance URL. 4. Authorization callback URL: ``` https:///api/backend/integrations/oauth/callback ``` 5. Click **Register application**. ### 2 — Get credentials On the app page, note the **Client ID**. Click **Generate a new client secret** and copy it. ### 3 — Enter in Noxus Admin In **Admin → Integrations → OAuth Providers → GitHub**, enter the Client ID and Client Secret. ### 1 — Create a HubSpot app 1. Go to [developers.hubspot.com](https://developers.hubspot.com) → **Manage apps → Create app**. 2. Under **Auth**, find your **Client ID** and **Client Secret**. 3. Add a redirect URL: ``` https:///api/backend/integrations/oauth/callback ``` 4. Under **Scopes**, add the CRM scopes you need (contacts, companies, deals). ### 2 — Enter in Noxus Admin In **Admin → Integrations → OAuth Providers → HubSpot**, enter the Client ID and Client Secret. ### 1 — Create an Airtable OAuth app 1. Go to [airtable.com/create/oauth](https://airtable.com/create/oauth) → **Register new OAuth integration**. 2. Redirect URI: ``` https:///api/backend/integrations/oauth/callback ``` 3. Select required scopes: `data.records:read`, `data.records:write`, `data.schemas:read`. ### 2 — Get credentials Copy the **Client ID** and **Client Secret** from the app page. ### 3 — Enter in Noxus Admin In **Admin → Integrations → OAuth Providers → Airtable**, enter the Client ID and Client Secret. *** ## Redirect URI reference All providers require the same callback URL: ``` https:///api/backend/integrations/oauth/callback ``` Replace `` with the actual hostname of your Noxus instance (e.g. `app.noxus.ai` or `noxus.yourcompany.com`). *** ## Troubleshooting The redirect URI registered with the provider does not exactly match the one Noxus sends. Ensure there are **no trailing slashes**, the protocol is `https://`, and the path is `/api/backend/integrations/oauth/callback`. The Client ID or Client Secret is incorrect. Re-copy the values from the provider's developer console — make sure there are no leading/trailing spaces. Ensure the **Client ID** field is not empty. If the provider requires a Tenant ID (Microsoft), make sure it is also filled in. This means the OAuth app's consent screen is in **testing/unverified** mode with the provider. For Google, publish the app in the OAuth consent screen settings. For others, complete the provider's app verification process. # Authentication Source: https://docs.noxus.ai/integrations/authentication Understanding authentication methods for integrations Noxus supports multiple authentication methods to securely connect to external services. ## Common Authentication Methods **User-delegated access with secure token exchange** ### How It Works ```mermaid theme={null} sequenceDiagram participant U as User participant N as Noxus participant S as Service Provider U->>N: Click "Connect" N->>S: Redirect to consent screen S->>U: Display permissions U->>S: Approve S->>N: Return authorization code N->>S: Exchange for access token S->>N: Return token N->>N: Store encrypted ``` ### Characteristics | Aspect | Details | | :------------------- | :----------------------------------------- | | **Security** | No password sharing, industry standard | | **Permissions** | Granular control, user-approved | | **Token Management** | Automatic refresh | | **Revocation** | User can revoke anytime | | **Best For** | User-specific actions, personal automation | ### Supported Services Workspace services 365 services Workspace integration Repository access Workspace databases Profile and posts OAuth is the most secure method and is recommended for all services that support it. **Simple token-based authentication** ### How It Works ```mermaid theme={null} graph LR A[Generate Key in Service] --> B[Enter in Noxus] B --> C[Store Encrypted] C --> D[Use in API Requests] ``` ### Characteristics | Aspect | Details | | :-------------- | :----------------------------------- | | **Setup** | Simple, no redirect flow | | **Rotation** | Manual rotation required | | **Permissions** | Service-level permissions | | **Management** | Per-application keys | | **Best For** | Developer tools, simple integrations | ### Supported Services Personal API keys Personal API keys Custom API keys (optional) Custom API keys (optional) API keys should be rotated regularly and stored securely. Never commit them to code. **Pre-shared credentials from a provider's developer portal** ### How It Works ```mermaid theme={null} sequenceDiagram participant A as Admin participant P as Provider Admin Portal participant N as Noxus participant API as Provider API A->>P: Create Connected App / Private App A->>P: Configure scopes & permissions P->>A: Issue Client ID, Secret, and/or Access Token A->>N: Paste credentials + instance URL N->>API: Authenticated API requests API->>N: Response ``` ### Characteristics | Aspect | Details | | :------------------- | :------------------------------------------------------- | | **Setup** | Manual app/integration creation in the provider's admin | | **User Interaction** | None after setup — credentials are tenant-scoped | | **Permissions** | Defined when the app is created in the provider | | **Token Lifetime** | Usually long-lived; rotation is manual | | **Best For** | Enterprise systems, providers without OAuth, custom apps | ### Supported Services Instance URL + Access Token Base URL + Client ID + Client Secret Shop Domain + Admin API Token Phone Number ID + Access Token Bot Token + Signing Secret Static access tokens don't auto-refresh. Rotate them on a regular cadence and revoke promptly when team members leave. **Application-level access for Microsoft services** ### How It Works ```mermaid theme={null} sequenceDiagram participant A as Admin participant AZ as Azure Portal participant N as Noxus participant MS as Microsoft API A->>AZ: Register app A->>AZ: Configure permissions A->>AZ: Grant admin consent A->>AZ: Generate secret A->>N: Enter credentials N->>MS: Request token MS->>N: Return token N->>MS: API requests ``` ### Characteristics | Aspect | Details | | :------------------- | :------------------------------------ | | **Access Level** | Organization-wide | | **Setup** | Admin configuration required | | **User Interaction** | None after setup | | **Permissions** | Application permissions | | **Best For** | Background automation, cross-user ops | ### When to Use * Background automation * Cross-user operations * Scheduled tasks * System integrations * Azure administrator access * Admin consent for permissions * Client secret management * Organization-wide scope Service Principal is ideal for automated, organization-wide operations without user context. *** ## Setup Examples OAuth setup for Gmail, Drive, Sheets, Docs, and Calendar OAuth setup for workspace channels and messaging API key setup for bases, tables, and records Service Principal setup for Outlook, Teams, and SharePoint *** ## Security & Compliance All credentials encrypted at rest with database-level encryption Automatic OAuth token refresh and secure storage Tokens isolated per workspace with tenant segregation SOC 2 Type II, GDPR-compliant data handling *** ## Best Practices * Test with sandbox accounts during development * Use separate credentials for dev/staging/prod * Handle errors gracefully with continue-on-error * Monitor rate limits to avoid throttling * Rotate credentials regularly * Audit integration access periodically * Remove unused integrations * Use read-only scopes when possible * Cache API responses when appropriate * Batch requests when APIs support it * Use filters to reduce data transfer * Monitor execution time # Integration Catalog Source: https://docs.noxus.ai/integrations/catalog Browse all 30+ integrations available on Noxus 🚧 The full integration catalog is under construction. New integration guides are coming soon. In the meantime, browse integrations by category: Outlook, Teams, SharePoint, Dynamics 365 Gmail, Drive, Sheets, Docs, Calendar Jira and Confluence GitHub, Linear, GitLab Slack, Notion, Discord, Telegram Full overview with authentication and setup guides # Integrations Source: https://docs.noxus.ai/integrations/overview Connect Noxus to external services and platforms Noxus integrates with 30+ external services, connecting your flows to the tools you already use. ## How Integrations Work In your workspace control, go to the Integrations section Choose the service you want to connect Log in with your account (OAuth) or provide API credentials Approve the scopes/permissions Noxus requires Integration nodes can now access your connected account *** ## Integration Categories Gmail, Drive, Sheets, Docs, Calendar Outlook, OneDrive, SharePoint, Teams Channels, DMs, file sharing Databases, pages, blocks Bases, tables, records Spaces, pages, content Repos, issues, PRs, commits Issues, projects, teams Issues, projects, workflows Repositories, merge requests Bot integration, messaging Server notifications, bots Business API integration Messages, channels, files Events, bookings Responses, forms CRM, Field Service, Business Central ERP operations Posts, company pages Posts, media Videos, metadata Videos, channels, transcripts *** ## Authentication Overview Different integrations use different authentication methods: Most common - Google, Microsoft, Slack, GitHub Simple token auth - Airtable, Linear App-level access - Microsoft 365, Dynamics 365 Detailed guide to authentication methods *** ## Managing Integrations Go to Settings → Integrations Click Connect next to desired service Follow OAuth flow or enter credentials Test connection with a simple flow **OAuth Integrations:** * Tokens refresh automatically * Re-authenticate if permissions change **API Key Integrations:** * Update keys in integration settings * Keys encrypted immediately upon save Go to Settings → Integrations Click Disconnect next to the service Flows using this integration will fail until reconnected Disconnecting revokes tokens on the external service and removes credentials from Noxus. *** ## Security & Privacy * All credentials encrypted at rest * Database-level encryption * Separate encryption keys per tenant * No plain-text storage * Automatic OAuth token refresh * Secure token storage in database * Tokens isolated per workspace * Revocable at any time * Request only necessary scopes * Users can review permissions before granting * Granular permission control * Regular security audits * SOC 2 Type II compliant storage * GDPR-compliant data handling * Audit logs for all integration access * Tenant data isolation *** ## Common Providers OAuth and Service Principal authentication for Outlook, Teams, SharePoint, and Dynamics 365 OAuth authentication for Gmail, Drive, Sheets, Docs, and Calendar API token authentication for Jira and Confluence GitHub, Linear, and GitLab integration guides Slack, Notion, Discord, and Telegram bots Browse the complete catalog *** ## Need a Custom Integration? Many services can be accessed via their REST APIs Use Webhook Trigger nodes for incoming data Contact support to request new integrations Build custom integration nodes # Atlassian Source: https://docs.noxus.ai/integrations/providers/atlassian Connect to Jira and Confluence with API token authentication Noxus integrates with Atlassian products (Jira and Confluence) using API token authentication for secure, organization-wide access. ## Supported Atlassian Services | Service | Authentication | Key Capabilities | | :------------- | :------------- | :----------------------------------------------------- | | **Jira** | API Token | Create/update issues, manage projects, track workflows | | **Confluence** | API Token | Create/edit pages, manage spaces, search content | *** ## Authentication: API Token ### Description Atlassian integrations use API tokens for authentication. An API token is a secure credential that allows Noxus to access your Atlassian resources without storing your password. ### When to Use * Automated issue management * Documentation workflows * Project tracking automation * Team collaboration flows * **Permission Type**: User-level API token * **Setup**: Generate token in Atlassian account * **Access Scope**: Based on user's permissions * **Best For**: Automation and integrations ### Authentication Flow ```mermaid theme={null} sequenceDiagram participant U as User/Admin participant A as Atlassian Account participant N as Noxus participant AJ as Atlassian API Note over U,AJ: One-Time Setup U->>A: Generate API token A->>U: Provide token U->>N: Enter credentials N->>N: Store token securely Note over U,AJ: When running a flow... N->>AJ: API request with token AJ->>N: Return data ``` *** ## Setup Process Navigate to [id.atlassian.com](https://id.atlassian.com) Go to **Security** → **API tokens** Click **Create API token** Enter a label (e.g., "Noxus Integration") Copy the token value immediately (it won't be shown again) Store the API token securely. You won't be able to retrieve it after leaving the page. Go to Integrations in your Noxus workspace Choose Jira or Confluence Provide: * **Email**: Your Atlassian account email * **API Token**: The token you generated * **Site URL**: Your Atlassian site URL (e.g., `yourcompany.atlassian.net`) Click **Test** to verify the connection Click **Save** to complete the setup *** ## Jira Integration ### Capabilities **Create and Update Issues:** * Create new issues with custom fields * Update issue status and assignees * Add comments and attachments * Link related issues * Set priority and labels **Search and Query:** * Search issues with JQL * Filter by project, status, assignee * Get issue details and history * Track issue changes **Project Management:** * List all accessible projects * Get project details and configuration * Access project boards * Manage project components * View project workflows **Automated Workflows:** * Transition issues between statuses * Assign issues based on criteria * Create sub-tasks automatically * Update custom fields * Sync with external systems ### Common Use Cases **Create Jira issues from various sources:** ```mermaid theme={null} graph LR A[Customer Email] --> B[Extract Info] B --> C[Create Jira Issue] C --> D[Notify Team] ``` * Support requests → Jira tickets * Bug reports from forms * Feature requests from Slack * Automated task creation **Keep Jira in sync with other systems:** ```mermaid theme={null} graph LR A[External System] --> B[Check Status] B --> C[Update Jira] C --> D[Notify Stakeholders] ``` * Sync with GitHub issues * Update from CI/CD pipelines * Reflect changes from other tools **Generate automated reports:** ```mermaid theme={null} graph LR A[Schedule] --> B[Query Jira] B --> C[Generate Report] C --> D[Distribute] ``` * Sprint summaries * Issue statistics * Team performance metrics * SLA tracking *** ## Confluence Integration ### Capabilities **Create and Edit Pages:** * Create pages in any space * Update page content * Add labels and metadata * Manage page hierarchy * Version tracking **Content Operations:** * Read page content * Export to different formats * Search across spaces * Attach files **Space Management:** * List accessible spaces * Get space details * Create new spaces (if permissions allow) * Manage space permissions * Access space content **Content Search:** * Search across all spaces * Filter by space, label, or content type * Full-text search * Recently updated content * Access page metadata ### Common Use Cases **Automatically create documentation:** ```mermaid theme={null} graph LR A[Data Source] --> B[Generate Content] B --> C[Create Confluence Page] C --> D[Notify Team] ``` * API documentation from specs * Meeting notes from transcripts * Status reports * Knowledge base articles **Sync content between systems:** ```mermaid theme={null} graph LR A[External Source] --> B[Transform Content] B --> C[Update Confluence] C --> D[Index for Search] ``` * Import documentation * Update from GitHub wikis * Sync with knowledge bases **Search Confluence from flows:** ```mermaid theme={null} graph LR A[User Query] --> B[Search Confluence] B --> C[Return Results] C --> D[Present to User] ``` * Find relevant documentation * Answer questions from pages * Extract information * Build internal search tools *** ## Security Best Practices * Rotate tokens regularly (every 90-180 days) * Use separate tokens for different integrations * Revoke unused tokens * Monitor token usage * Use accounts with appropriate permissions * Don't use admin accounts unless necessary * Review Jira/Confluence permissions * Audit automation activities *** ## Troubleshooting **Possible Causes:** * Invalid API token * Token expired or revoked * Incorrect email address * Wrong site URL **Solutions:** * Generate a new API token * Verify email matches token owner * Check site URL format (e.g., `company.atlassian.net`) * Test connection with Atlassian API directly **Possible Causes:** * Insufficient Jira/Confluence permissions * Project/space access restrictions * Issue type not allowed * Field restrictions **Solutions:** * Verify user has required permissions * Check project/space access settings * Review issue type scheme * Confirm field permissions **Possible Causes:** * Too many API requests * Concurrent flow executions * Bulk operations **Solutions:** * Implement delays between requests * Use batch operations where available * Reduce flow execution frequency * Contact Atlassian about rate limits *** ## Next Steps GitHub, Linear, and more Slack, Notion, and team tools Browse the complete catalog # Collaboration Tools Source: https://docs.noxus.ai/integrations/providers/collaboration Connect Slack, Notion, and other team collaboration platforms Integrate Noxus with your team's collaboration tools for automated communication, content management, and workflow orchestration. ## Supported Collaboration Tools | Tool | Authentication | Key Capabilities | | :----------- | :--------------------------- | :----------------------------------------------- | | **Slack** | OAuth / Bot Token | Messaging, channels, file sharing, notifications | | **Notion** | OAuth / Internal Integration | Databases, pages, blocks, content management | | **Discord** | Bot Token | Messages, channels, server management | | **Telegram** | Bot Token | Send messages, manage bots, group operations | *** ## Slack Integration ### Authentication Methods **User or bot authentication via OAuth 2.0** **Setup Process:** Navigate to Integrations → Slack → Connect Choose your Slack workspace Review and approve requested permissions Select which channels Noxus can access Connection is ready to use **Permissions:** * Read/write messages * Upload files * Manage channels * User information access **Custom bot with fine-grained control** **Setup Process:** Go to [api.slack.com/apps](https://api.slack.com/apps) → Create New App Add bot user and configure permissions Install app to your Slack workspace Copy the Bot User OAuth Token Enter bot token in Noxus integration settings Bot tokens provide more control over permissions and are recommended for production use. ### Capabilities **Send and receive messages:** * Post messages to channels * Send direct messages * Thread replies * Update or delete messages * React to messages with emojis * Format with blocks and attachments **Manage channels and conversations:** * List channels * Create new channels * Archive channels * Invite users to channels * Get channel history **Share and manage files:** * Upload files to channels * Share files with users * Get file details * Download files **User and team management:** * Get user information * List workspace members * Lookup by email * User presence status ### Common Use Cases **Automated Slack notifications:** ```mermaid theme={null} graph LR A[Event Trigger] --> B[Format Message] B --> C[Post to Slack] C --> D[Thread Updates] ``` **Examples:** * Flow completion notifications * Error alerts * Daily summaries * Customer activity updates **Build Slack bots with AI:** ```mermaid theme={null} graph LR A[Slack Message] --> B[Agent Process] B --> C[Search Knowledge] C --> D[Execute Flow] D --> E[Reply in Slack] ``` **Examples:** * Support bot answering questions * HR assistant * IT helpdesk * Internal search tool **Connect Slack to business processes:** ```mermaid theme={null} graph LR A[Slack Command] --> B[Validate Request] B --> C[Execute Business Logic] C --> D[Update Systems] D --> E[Confirm in Slack] ``` **Examples:** * Approval workflows * Request management * Status updates * Report generation *** ## Notion Integration ### Authentication: OAuth Notion uses OAuth 2.0 for secure workspace access. Navigate to Integrations → Notion → Connect Choose your Notion workspace Choose which pages Noxus can access Click "Allow access" Connection is ready ### Capabilities **Work with Notion databases:** * Query databases * Create new entries * Update existing entries * Filter and sort * Retrieve database schema **Create and manage pages:** * Create new pages * Update page content * Add blocks (text, images, embeds) * Manage page properties * Archive pages **Rich content manipulation:** * Read page content * Append blocks * Update block content * Handle different block types * Export content ### Common Use Cases **Sync customer data with Notion:** ```mermaid theme={null} graph LR A[CRM Update] --> B[Transform Data] B --> C[Update Notion DB] C --> D[Notify Team] ``` * Customer information sync * Deal pipeline management * Contact database updates **Auto-generate documentation:** ```mermaid theme={null} graph LR A[Data Source] --> B[Generate Content] B --> C[Create Notion Page] C --> D[Share with Team] ``` * API documentation * Meeting notes * Status reports * Project updates **Manage tasks and projects:** ```mermaid theme={null} graph LR A[Task Created] --> B[Add to Notion] B --> C[Assign Owner] C --> D[Set Reminders] ``` * Task tracking * Project management * Team assignments * Progress monitoring *** ## Telegram & Discord ### Telegram Bot **Authentication:** Bot Token from BotFather Message [@BotFather](https://t.me/botfather) on Telegram Follow instructions to create a new bot Copy the HTTP API token provided Enter token in Telegram integration **Use Cases:** * Customer notifications * Alert systems * Interactive bots * Group management ### Discord Bot **Authentication:** Bot Token from Discord Developer Portal Go to [Discord Developer Portal](https://discord.com/developers/applications) Create a bot user in your application Copy the bot token Enter token in Discord integration Generate OAuth URL and add bot to your server **Use Cases:** * Community management * Automated moderation * Event notifications * Server administration *** ## Security Best Practices * Store tokens securely * Rotate tokens regularly * Use separate tokens per environment * Never commit tokens to code * Request minimum required permissions * Review app permissions regularly * Revoke unused integrations * Monitor integration activity *** ## Troubleshooting **Common Problems:** * Bot not in channel * Missing permissions * Token expired * Rate limits **Solutions:** * Invite bot to required channels * Review and add missing scopes * Reinstall app or regenerate token * Implement rate limiting **Common Problems:** * Pages not shared with integration * Missing database permissions * Workspace access revoked **Solutions:** * Share pages with the integration * Grant database access * Reconnect the integration **Common Problems:** * Invalid token * Bot offline * Network issues * Permission changes **Solutions:** * Verify token is valid * Check bot status * Test network connectivity * Review recent permission changes *** ## Next Steps Connect to Microsoft services Integrate with Google services Browse the complete catalog # Developer Tools Source: https://docs.noxus.ai/integrations/providers/developer-tools Integrate with GitHub, Linear, and development platforms Connect Noxus to your development tools for automated code management, issue tracking, and project coordination. ## Supported Developer Tools | Tool | Authentication | Key Capabilities | | :--------- | :---------------------------- | :----------------------------------------------- | | **GitHub** | OAuth / Personal Access Token | Repositories, issues, PRs, actions, code reviews | | **Linear** | API Key | Issues, projects, teams, workflows | | **GitLab** | Personal Access Token | Repositories, merge requests, pipelines | *** ## GitHub Integration ### Authentication Methods **User-delegated access to GitHub resources** Go to Integrations in Noxus Click Connect on GitHub integration Sign in to GitHub and authorize Noxus Choose which repositories and permissions to grant Connection is ready to use **Permissions:** * Repository access (read/write) * Issue management * Pull request operations * Workflow dispatch **Token-based authentication for fine-grained control** In GitHub: Settings → Developer settings → Personal access tokens → Generate new token Choose required scopes (repo, workflow, etc.) Copy the generated token Enter token in GitHub integration settings Verify the connection works Personal Access Tokens provide broad access. Use OAuth for better security when possible. ### Capabilities * Clone and fetch repositories * Read file contents * Create and update files * Manage branches * Access commit history * Create and update issues * Add comments and labels * Assign to users * Link to pull requests * Search and filter issues * Create pull requests * Review and comment on PRs * Approve or request changes * Merge pull requests * Trigger CI/CD workflows * Trigger GitHub Actions * Monitor workflow runs * Access run logs * Dispatch repository events ### Common Use Cases **AI-powered code review workflows:** ```mermaid theme={null} graph LR A[PR Created] --> B[Fetch Code Changes] B --> C[AI Analysis] C --> D[Post Review Comment] D --> E[Request Changes/Approve] ``` * Analyze code changes with AI * Check for security issues * Enforce coding standards * Automated suggestions **Automate issue management:** ```mermaid theme={null} graph LR A[External Input] --> B[Process Data] B --> C[Create GitHub Issue] C --> D[Assign & Label] D --> E[Notify Team] ``` * Create issues from customer feedback * Auto-assign based on content * Label and categorize automatically * Link to related issues **Automate release processes:** ```mermaid theme={null} graph LR A[Tag Created] --> B[Generate Changelog] B --> C[Create Release] C --> D[Notify Stakeholders] ``` * Generate release notes * Update documentation * Notify stakeholders * Trigger deployment workflows *** ## Linear Integration ### Authentication: API Key Linear uses API key authentication for secure access to your workspace. In Linear: Settings → API → Create new key Copy the generated API key Go to Integrations → Linear → Enter API key Save the connection ### Capabilities * Create and update issues * Manage issue status and priority * Assign to team members * Add comments and attachments * Set estimates and due dates * Create and manage projects * Track project progress * Organize with milestones * View project roadmaps * Access team information * View team members * Get team workflows * Track team capacity ### Common Use Cases **Automatically triage and route issues:** ```mermaid theme={null} graph LR A[Issue Created] --> B[AI Categorize] B --> C[Assign Team] C --> D[Set Priority] D --> E[Add Labels] ``` * AI-powered categorization * Smart team assignment * Priority detection * Auto-labeling **Sync with other tools:** ```mermaid theme={null} graph LR A[GitHub Issue] --> B[Transform Data] B --> C[Create Linear Issue] C --> D[Keep in Sync] ``` * GitHub → Linear sync * Slack → Linear issue creation * Email → Linear tasks *** ## Security Best Practices * Generate separate tokens per integration * Rotate tokens regularly * Revoke unused tokens * Monitor token usage * Use minimum required permissions * Regular permission audits * Separate tokens for different environments * Document token purposes *** ## Troubleshooting **Possible Causes:** * Invalid API token or key * Token revoked or expired * Incorrect credentials * Network connectivity issues **Solutions:** * Generate a new token * Verify credentials are correct * Check token hasn't been revoked * Test connection manually **Possible Causes:** * Insufficient permissions * Repository/project access restrictions * Organization policies * Token scope limitations **Solutions:** * Verify token has required scopes * Check repository/project permissions * Review organization settings * Use OAuth for broader access (GitHub) **Possible Causes:** * Too many API requests * Concurrent operations * Bulk data operations **Solutions:** * Implement rate limiting in flows * Use batch operations * Add delays between requests * Upgrade API limits if available *** ## Next Steps Connect Slack, Notion, and team tools Integrate with Microsoft services Browse the complete catalog # Google Workspace Source: https://docs.noxus.ai/integrations/providers/google Connect to Google services with OAuth authentication Noxus integrates with Google Workspace services using OAuth 2.0 authentication, allowing you to access Gmail, Drive, Sheets, Docs, Calendar, and more. ## Supported Google Services | Service | Authentication | Key Capabilities | | :------------------ | :------------- | :-------------------------------------------------- | | **Gmail** | OAuth 2.0 | Send/receive emails, read threads, manage labels | | **Google Drive** | OAuth 2.0 | Upload/download files, folder management, sharing | | **Google Sheets** | OAuth 2.0 | Read/write data, create sheets, manage workbooks | | **Google Docs** | OAuth 2.0 | Create/edit documents, export formats | | **Google Calendar** | OAuth 2.0 | Create events, manage calendars, check availability | *** ## Authentication: OAuth 2.0 ### Description Google integrations use OAuth 2.0 to authenticate as a specific user. Users grant permission through Google's consent screen, and Noxus can then access the resources that user has permission to view or modify. ### When to Use * User-specific actions (sending email as a user) * Personal file access * Individual calendar management * Actions requiring user attribution * **Permission Type**: User-delegated * **Setup**: Interactive consent flow * **Access Scope**: User's accessible resources * **Best For**: Personal automation, user workflows ### Authentication Flow ```mermaid theme={null} sequenceDiagram participant U as User participant NF as Noxus Frontend participant G as Google OAuth participant NB as Noxus Backend participant GA as Google API Note over U,GA: Initial Setup U->>NF: Click "Connect Google Service" NF->>G: Redirect to Google consent screen G->>U: Display permission request U->>G: Approve permissions G->>NF: Return authorization code NF->>NB: Complete connection NB->>NB: Store tokens securely Note over U,GA: When running a flow... NB->>G: Request/refresh access token G->>NB: Return valid token NB->>GA: API request with token GA->>NB: Return data ``` ## Setup Process Go to Integrations in your Noxus workspace Choose the Google service (Gmail, Drive, Sheets, Docs, or Calendar) Click the Connect button Sign in with your Google account Review the requested permissions carefully Click "Allow" to grant permissions Connection is active and ready to use in flows *** ## Permissions by Service **Scopes Required:** * `gmail.readonly` - Read email and settings * `gmail.send` - Send email on your behalf * `gmail.modify` - Manage drafts and send email **Capabilities:** * Read emails and threads * Send emails * Create and manage labels * Search messages * Manage drafts **Scopes Required:** * `drive.readonly` - View files and folders * `drive.file` - View and manage files created by Noxus * `drive` - Full Drive access **Capabilities:** * Upload and download files * Create and manage folders * Share files and folders * Search Drive * Manage permissions **Scopes Required:** * `spreadsheets.readonly` - View spreadsheets * `spreadsheets` - Create and edit spreadsheets **Capabilities:** * Read sheet data * Write data to cells * Create new sheets * Format cells * Manage worksheets **Scopes Required:** * `documents.readonly` - View documents * `documents` - Create and edit documents **Capabilities:** * Read document content * Create new documents * Edit existing documents * Export to different formats **Scopes Required:** * `calendar.readonly` - View calendars * `calendar.events` - Manage events * `calendar` - Full calendar access **Capabilities:** * Create calendar events * Read event details * Update events * Check availability * Manage calendars *** ## Common Use Cases **Email Processing Workflows:** * Monitor inbox for specific emails * Automatically respond to inquiries * Extract data from email attachments * Route emails based on content * Send bulk personalized emails **Example Flow:** ```mermaid theme={null} graph LR A[Email Received] --> B[Extract Attachment] B --> C[Process Document] C --> D[Send Reply] ``` **File Management:** * Sync files to knowledge bases * Backup important documents * Process uploaded files * Generate and upload reports * Share files automatically **Example Flow:** ```mermaid theme={null} graph LR A[Schedule Trigger] --> B[Fetch from Drive] B --> C[Process Files] C --> D[Upload to KB] ``` **Data Workflows:** * Export data to Sheets * Import data for processing * Generate reports * Update tracking sheets * Analyze spreadsheet data **Example Flow:** ```mermaid theme={null} graph LR A[Data Source] --> B[Transform Data] B --> C[Write to Sheets] C --> D[Notify Team] ``` **Event Management:** * Schedule meetings automatically * Check availability * Send calendar invites * Sync events across systems * Automated reminders **Example Flow:** ```mermaid theme={null} graph LR A[New Request] --> B[Check Availability] B --> C[Create Event] C --> D[Send Invite] ``` *** ## Security Best Practices * Request only necessary scopes * Regularly review connected apps * Remove unused connections * Monitor access logs * Tokens stored encrypted * Automatic token refresh * Revoke access when needed * Audit token usage *** ## Troubleshooting **Possible Causes:** * Invalid Google account * Permissions not granted * OAuth flow interrupted * Network connectivity issues **Solutions:** * Try reconnecting with a different account * Ensure all permissions are approved * Complete the OAuth flow without closing windows * Check network connection **Possible Causes:** * Missing required scopes * User revoked access * Resource access restrictions * Admin disabled app access **Solutions:** * Reconnect to grant additional scopes * Check Google account permissions * Verify resource sharing settings * Contact Google Workspace admin **Possible Causes:** * API rate limits reached * Too many concurrent requests * Daily quota exceeded **Solutions:** * Implement rate limiting in flows * Reduce request frequency * Use batch operations when available * Upgrade Google Workspace plan if needed *** ## Next Steps Connect Outlook, Teams, and OneDrive Integrate Jira and Confluence Browse the complete catalog # Azure & Microsoft 365 Source: https://docs.noxus.ai/integrations/providers/microsoft Connect to Microsoft services with OAuth or Service Principal authentication Noxus supports two authentication methods for Microsoft 365 integrations: 1. **OAuth (User-Delegated)** - Authenticate as a specific user 2. **Service Principal (Application)** - Authenticate as an application with organization-wide access Both methods connect to Microsoft's identity platform to access Microsoft Graph API and other Microsoft services. ## Supported Microsoft 365 Services | Service | OAuth | Service Principal | | :-------------------------------- | :---- | :---------------- | | **Outlook** | ✅ Yes | ✅ Yes | | **Microsoft Teams** | ✅ Yes | ✅ Yes | | **OneDrive** | ✅ Yes | ✅ Yes | | **SharePoint** | ✅ Yes | ✅ Yes | | **Dynamics 365 CRM** | ❌ No | ✅ Yes | | **Dynamics 365 Field Service** | ❌ No | ✅ Yes | | **Dynamics 365 Business Central** | ❌ No | ✅ Yes | *** ## Authentication Method 1: OAuth (User-Delegated) ### Description OAuth authentication allows Noxus to perform actions on behalf of a specific user. The user grants permission through an interactive consent screen, and Noxus can then access resources that user has access to. ### When to Use * Actions should appear as coming from a specific user * Access should be limited to what the user can see * User accountability is required for audit trails * You only need access to one user's data * **Permission Type**: Delegated (user context) * **User Interaction**: Required during setup * **Access Scope**: Limited to user's accessible resources * **Token Management**: Automatic refresh * **Best For**: User-specific workflows, personal automation ### Authentication Flow ```mermaid theme={null} sequenceDiagram participant U as User participant NF as Noxus Frontend participant ME as Microsoft Entra ID participant NB as Noxus Backend participant MG as Microsoft Graph API Note over U,MG: Initial Setup U->>NF: Click "Connect Service" NF->>ME: Redirect to consent screen ME->>U: Display permission request U->>ME: Approve permissions ME->>NF: Return authorization code NF->>NB: Complete connection NB->>NB: Store connection securely Note over U,MG: When running a workflow... NB->>ME: Request access token ME->>NB: Return valid token NB->>MG: API request with token MG->>NB: Return data ``` ### Setup Process Go to Integrations in your Noxus workspace Select the Microsoft service you want to connect (Outlook, Teams, OneDrive, or SharePoint) Click the Connect button for OAuth authentication Sign in with your Microsoft account when prompted Review and approve the requested permissions Connection is now active and ready to use *** ## Authentication Method 2: Service Principal (Application) ### Description Service Principal authentication uses an Azure application identity with administrator-approved permissions. This enables organization-wide access without requiring individual user consent for each operation. ### When to Use * Running background automation without user involvement * Need fine-grained control of which permissions/entities can be accessed * Access to resources across multiple users * Organization-wide operations * Scheduled tasks and system integrations * **Permission Type**: Application (organization context) * **User Interaction**: None required after setup * **Access Scope**: Organization-wide (based on granted permissions) * **Token Management**: Automatic * **Best For**: Background automation, cross-user operations ### Authentication Flow ```mermaid theme={null} sequenceDiagram participant AA as Azure Administrator participant AP as Azure Portal participant NP as Noxus Platform participant ME as Microsoft Entra ID participant MG as Microsoft Graph API Note over AA,MG: One-Time Setup AA->>AP: Register application AA->>AP: Configure API permissions AA->>AP: Grant admin consent AA->>AP: Generate client secret AA->>NP: Enter credentials Note over AA,MG: Automated Execution NP->>ME: Request token (client credentials) ME->>NP: Return access token NP->>MG: API request with token MG->>NP: Return data ``` *** ## Azure Setup Requirements ### Step 1: Register an Application Go to [Azure Portal](https://portal.azure.com) Go to Microsoft Entra ID → App registrations Click **New registration** * Enter a name for the application * Select "Accounts in this organizational directory only" * Click **Register** ### Step 2: Configure API Permissions Add the following permissions based on the services you need: **For Outlook, Teams, OneDrive, SharePoint:** | Service | Required Permissions | | :------------- | :---------------------------------------------------------------------------- | | **Outlook** | Mail.Read, Mail.Send, Mail.ReadWrite | | **Teams** | Team.ReadBasic.All, Channel.ReadBasic.All, ChannelMessage.Send | | **OneDrive** | Files.Read.All, Files.ReadWrite.All | | **SharePoint** | Sites.Read.All, Sites.ReadWrite.All (or Sites.Selected for restricted access) | **For Dynamics 365 services:** | Service | Required Permissions | | :------------------- | :------------------------------------------------------- | | **Dynamics 365 CRM** | user\_impersonation or appropriate Dataverse permissions | | **Field Service** | user\_impersonation or appropriate Dataverse permissions | | **Business Central** | Financials.ReadWrite.All | ### Step 3: Grant Admin Consent In your app registration, go to **API permissions** Click **Grant admin consent for \[Your Organization]** Confirm the action Admin consent is required for Service Principal authentication. Only Azure administrators can grant this consent. ### Step 4: Create Client Secret Go to **Certificates & secrets** Click **New client secret** Set an expiration period (6 months, 12 months, or 24 months) Copy the secret value immediately (it won't be shown again) Store the client secret securely. You won't be able to retrieve it after leaving the page. ### Step 5: Gather Required Information You will need: | Credential | Location | | :---------------- | :-------------------------------------- | | **Tenant ID** | Found in Microsoft Entra ID overview | | **Client ID** | Found in your app registration overview | | **Client Secret** | The value you copied in Step 4 | *** ## Noxus Configuration Go to Integrations in your Noxus workspace Select the Service Principal version of your desired service Enter your credentials: * Tenant ID * Client ID * Client Secret For each integration there may be additional configurations, such as granular permissions used, or resource limitations Click Save to complete the connection *** ## Comparison: OAuth vs Service Principal ```mermaid theme={null} graph TB subgraph OAuth["OAuth (User-Delegated)"] O1[User Identity] O2[Interactive Setup] O3[User-Scoped Access] O4[Personal Resources] end subgraph SP["Service Principal (Application)"] S1[Application Identity] S2[Admin Configuration] S3[Organization-Wide Access] S4[All Accessible Resources] end OAuth -.->|Best for| U1[User-initiated workflows] SP -.->|Best for| U2[Automated processes] ``` ### Feature Comparison | Feature | OAuth | Service Principal | | :------------------- | :--------------------- | :----------------------------------- | | **Identity** | Individual user | Application | | **Setup** | User clicks to connect | Admin configures in Azure | | **Consent** | User approves | Admin pre-approves | | **Access Level** | User's resources only | Organization-wide | | **User Interaction** | Required at setup | Not required | | **Typical Use Case** | Personal automation | Background jobs, org-wide operations | | **Maintenance** | Minimal | Secret rotation required | ### Decision Tree ```mermaid theme={null} graph TD A[Which authentication method?] --> B{Who performs the action?} B -->|Specific user| C{Need user context?} B -->|System/Automation| D{Organization-wide access needed?} C -->|Yes| E[OAuth] C -->|No| D D -->|Yes| F[Service Principal] D -->|No| E ``` *** ## Advanced: SharePoint Sites.Selected Permission For use-cases requiring restricted access to specific SharePoint sites, Noxus supports the `Sites.Selected` permission model. **Full Access Model** * Access to all SharePoint sites in the organization * Simpler configuration * Broader permissions * No site-specific configuration needed **Restricted Access Model** * Access only to specified sites * Better security posture * Granular control * Requires configuration of allowed site URLs ### Configuration Options | Setting | Description | | :-------------------------------- | :------------------------------------------------------ | | **Allowed Site URLs** | List of SharePoint site URLs the integration can access | | **Use Sites.Selected Permission** | When enabled, only fetches the specified sites | Use `Sites.Selected` for better security when you don't need access to all SharePoint sites. *** ## Dynamics 365 Integrations Dynamics 365 services only support Service Principal authentication. ### Dynamics 365 CRM and Field Service ```mermaid theme={null} graph LR N[Noxus] -->|Client Credentials| ME[Microsoft Entra ID] ME -->|Access Token| N N -->|Authenticated Request| D[Dataverse API] D -->|CRM Data| N D -->|Field Service Data| N ``` **Supported Operations:** * Accounts, Contacts, Leads, Opportunities (CRM) * Work Orders, Bookings, Resources (Field Service) * Custom entities via Dataverse ### Dynamics 365 Business Central ```mermaid theme={null} graph LR N[Noxus] -->|Client Credentials| ME[Microsoft Entra ID] ME -->|Access Token| N N -->|Authenticated Request| BC[Business Central API] BC -->|Customers| N BC -->|Invoices| N BC -->|Sales Orders| N BC -->|Items| N ``` **Supported Operations:** * Customer management * Sales invoice creation and posting * Sales order management * Item and inventory queries * Payment tracking *** ## Security Best Practices **Protecting User-Delegated Access:** Regularly review connected applications in your Microsoft account Disconnect services you no longer use Be cautious about the permissions you approve Review activity logs for unexpected access **Securing Application Access:** Use the minimum permissions necessary for your use case Rotate client secrets before they expire Monitor API usage through Azure logs Consider using Sites.Selected for SharePoint when full access is not needed Document which applications have admin consent *** ## Troubleshooting **Possible Causes:** * Invalid credentials (Tenant ID, Client ID, or Client Secret) * Client secret expired * Missing API permissions * Admin consent not granted **Solutions:** * Verify credentials are correct * Generate a new client secret if expired * Check API permissions in Azure Portal * Ensure admin consent has been granted **Possible Causes:** * Missing required API permissions * Admin consent revoked * Resource access restrictions * User doesn't have access (OAuth) **Solutions:** * Review and add missing permissions in Azure * Re-grant admin consent * Check resource-level permissions * Verify user has access to the resource **Possible Causes:** * Client secret expired * App registration deleted * Permissions changed * User revoked consent (OAuth) **Solutions:** * Generate and update client secret * Verify app registration still exists * Review permission changes * Re-authenticate with OAuth *** ## Next Steps Connect Gmail, Drive, Sheets, and more Integrate Jira and Confluence Browse the complete catalog # Quick Setup Examples Source: https://docs.noxus.ai/integrations/quick-examples Step-by-step setup guides for common integrations Get started quickly with these step-by-step guides for the most popular integrations. ## Quick Setup Examples Settings → Integrations → Google Workspace → Connect Select Google account Review and click Allow Integration shows as Connected Settings → Integrations → Slack → Connect Choose Slack workspace Review permissions and click Allow Use `/invite @Noxus` in channels you want to use Go to airtable.com/account → Generate API key Copy the key (starts with `key...`) Settings → Integrations → Airtable → Paste key Click Connect Register app in Azure Portal with required permissions Grant admin consent for organization Create client secret in Azure Enter Tenant ID, Client ID, and Client Secret Complete Microsoft 365 setup guide In Salesforce: Setup → Apps → App Manager → New Connected App. Enable OAuth Settings and select the scopes you need. From the Connected App, generate an access token (or use an existing one issued for your org). Settings → Integrations → Salesforce → enter Instance URL (e.g. `https://yourcompany.my.salesforce.com`) and Access Token. Click Connect. Noxus validates the token against `/services/oauth2/userinfo`. Full Salesforce setup guide In your SAP system (e.g. SuccessFactors Admin Center → API Center, or Gateway Service Builder for OData), create an OAuth2 Client Application and grant the required permissions. Note the Base URL, Client ID, Client Secret, and Company ID (SuccessFactors only). Settings → Integrations → SAP → select product (S/4HANA, SuccessFactors, Ariba, Concur) and paste the credentials. Click Connect and run a test query to verify connectivity. Full SAP setup guide *** ## Common Providers Detailed guides for popular integrations: OAuth and Service Principal for Outlook, Teams, SharePoint, and Dynamics 365 OAuth for Gmail, Drive, Sheets, Docs, and Calendar API token for Jira and Confluence GitHub, Linear, GitLab integration guides Slack, Notion, Discord, and Telegram Complete catalog with all 30+ integrations # Troubleshooting Source: https://docs.noxus.ai/integrations/troubleshooting Common integration issues and solutions General guidelines for troubleshooting integration issues across all services. ## Common Issues ### Possible Causes * Invalid credentials (API key, OAuth token) * Expired authentication * Incorrect configuration * Network connectivity issues ### Solutions Check that credentials are correct and haven't expired Try disconnecting and reconnecting the integration Verify all required fields (URLs, IDs, etc.) are correct Ensure network connectivity to the external service For OAuth integrations, try clearing browser cache and cookies before reconnecting. ### Possible Causes * Missing required permissions * User revoked access * Admin restrictions * Resource access limitations ### Solutions Check that all required scopes were granted Disconnect and reconnect, approving all requested permissions Confirm user has access to the specific resources For enterprise accounts, verify admin hasn't blocked the integration Some integrations require admin approval. Contact your IT administrator if needed. ### Possible Causes * Too many API requests in short time * Concurrent flow executions * Bulk operations without throttling * Service quota exceeded ### Solutions * Add delays between API calls * Use batch operations when available * Implement exponential backoff * Reduce polling frequency * Track API usage in flow logs * Monitor rate limit headers * Set up alerts for rate limits * Review service quotas * Cache responses when possible * Filter data at source * Use pagination efficiently * Combine multiple operations ### Possible Causes * OAuth token expired (shouldn't happen with auto-refresh) * API key rotated * Client secret expired * Session timeout ### Solutions Go to Settings → Integrations → Reconnect For API keys, enter new credentials For Service Principals, check client secret expiration Run a test flow to verify connection ### Possible Causes * Integration not connected in workspace * Wrong workspace selected * Integration was disconnected * Node requires specific integration ### Solutions Verify integration is connected: Settings → Integrations Ensure you're in the correct workspace Connect the integration if disconnected Refresh the flow editor to see updated integrations ### Possible Causes * Permission issues * API changes * Filter or query errors * Data format mismatches ### Solutions * Verify read/write permissions * Check resource-level access * Review sharing settings * Test with simpler operations * Review filter settings * Check field mappings * Verify data formats match * Test with sample data * Check flow execution logs * Inspect node outputs * Test integration directly * Review API documentation *** ## Debugging Steps Verify integration shows as "Connected" in Settings → Integrations Examine execution logs for error messages and details Create a minimal test flow with just the integration node Confirm all required permissions are granted Verify the external service is operational (status pages) Check if credentials were rotated or permissions changed *** ## Service-Specific Issues **Common Issues:** | Issue | Solution | | :-------------------- | :------------------------------------------------------- | | Admin restrictions | Contact Google Workspace admin to allow third-party apps | | Quota exceeded | Check Google API quotas in Cloud Console | | Redirect URI mismatch | Contact Noxus support to verify OAuth configuration | | Account not eligible | Ensure using Google Workspace (not personal Gmail) | **Common Issues:** | Issue | Solution | | :---------------------- | :---------------------------------- | | Admin approval required | Forward consent request to IT admin | | Invalid tenant | Verify tenant ID is correct | | Client secret expired | Generate new secret in Azure Portal | | MFA issues | Complete MFA before connecting | **Common Issues:** | Issue | Solution | | :------------------ | :--------------------------------------------------- | | Bot not in channel | Use `/invite @Noxus` in channel | | Missing scope | Disconnect and reconnect with additional permissions | | Rate limited | Space out messages, check Slack tier limits | | Workspace not found | Verify workspace URL is correct | **Common Issues:** | Issue | Solution | | :------------------- | :--------------------------------------------------------- | | Repository not found | Update repository access in GitHub Settings → Applications | | API rate limit | Wait for rate limit reset (5000 requests/hour) | | Push rejected | Verify write permissions and branch protection rules | | Token expired | Reconnect OAuth or generate new Personal Access Token | *** ## Getting Help Verify external service is operational * Google Workspace Status * Microsoft 365 Status * GitHub Status * Slack Status Check provider-specific guides: * [Microsoft 365](/integrations/providers/microsoft) * [Google Workspace](/integrations/providers/google) * [Atlassian](/integrations/providers/atlassian) * [Developer Tools](/integrations/providers/developer-tools) Examine execution logs for detailed error messages and stack traces Reach out to Noxus support with: * Integration name * Error message * Flow execution logs * Steps to reproduce *** ## Best Practices for Reliability **Build resilient flows:** * Enable "Continue on Error" for integration nodes * Add retry logic with exponential backoff * Implement fallback paths * Log errors for debugging **Track integration health:** * Monitor success/failure rates * Set up alerts for repeated failures * Track API usage and quotas * Review logs regularly **Keep integrations healthy:** * Rotate credentials before expiration * Test integrations after service updates * Update permissions when needed * Remove unused connections *** Learn more about authentication methods # Overview Source: https://docs.noxus.ai/platform/agents/deployments Deploy and manage agents in production Deploy agents through multiple channels to meet your integration needs. Agent Deployments Interface ## Deployment Options **Deploy agents directly in the Noxus platform** * Web-based chat interface * Real-time conversation testing * Performance monitoring * Configuration management * Internal team assistants * Testing and development * Admin tools * Training and demos **Integrate agents into your applications via REST API** * Programmatic conversation management * Webhook notifications * Custom UI integration * Multi-channel deployment * Customer-facing chatbots * Mobile app integration * Custom interfaces * Multi-platform deployment **Example:** ```bash theme={null} # Create conversation curl -X POST https://api.noxus.ai/v1/conversations \ -H "Authorization: Bearer ${API_KEY}" \ -d '{"agent_id": "agent_123"}' # Send message curl -X POST https://api.noxus.ai/v1/conversations/conv_456/messages \ -H "Authorization: Bearer ${API_KEY}" \ -d '{"content": "Hello, I need help"}' ``` **Embed agents in Python applications** * Type-safe Python interface * Async/await support * Streaming responses * Event handling * Python applications * Data science workflows * Backend services * Automation scripts **Example:** ```python theme={null} from noxus_sdk.client import Client client = Client(api_key="your_api_key") # Create conversation conversation = client.conversations.create( agent_id="agent_123", name="Support Session" ) # Chat with agent response = conversation.chat("How do I reset my password?") print(response.content) # Stream responses for chunk in conversation.chat_stream("Tell me more"): print(chunk.content, end="", flush=True) ``` **Add pre-built chat widget to your website** * Drop-in JavaScript widget * Customizable styling * Mobile responsive * Conversation persistence * Website chat support * Product assistance * Lead qualification * Customer engagement **Example:** ```html theme={null} ``` **Deploy agents to messaging platforms** Connect your agent to popular messaging and collaboration tools so users can interact with it where they already work. Deploy your agent to Slack channels and DMs. Respond to mentions, direct messages, and participate in group conversations. Bring your agent into Microsoft Teams channels and chats for enterprise collaboration. Connect your agent to WhatsApp for customer communication through the world's most popular messaging app. Deploy your agent as a Telegram bot for lightweight, fast interactions with users. ## Agent Configuration **Choose the right model for your use case:** | Model | Best For | Performance | | :-------------------------------------- | :-------------------------------- | :-------------------- | | **GPT-4 / Claude Opus / Gemini Ultra** | Complex reasoning, critical tasks | ⭐⭐⭐⭐⭐ Highest quality | | **GPT-4o / Claude Sonnet / Gemini Pro** | Balanced performance, general use | ⭐⭐⭐⭐ Great balance | | **GPT-3.5 / Mistral / Gemini Flash** | Simple tasks, high volume | ⭐⭐⭐ Fast & economical | **Fine-tune agent behavior:** | Setting | Range | Purpose | | :-------------- | :---------- | :------------------------------------------------- | | **Temperature** | 0.0 - 1.0 | Control creativity (0.0 = focused, 1.0 = creative) | | **Max Tokens** | 100 - 4000+ | Set response length limits | | **Timeout** | 10s - 300s | Maximum execution time | | **Streaming** | On/Off | Enable real-time response streaming | **Control usage and costs:** **Conversation Limits:** * Maximum messages per conversation * Token budget per conversation * Time-based expiration * Concurrent conversation limits **Rate Limits:** * Requests per minute * Requests per hour * Daily request caps * Per-user limits ## Past Chats Track and review all conversations with your agent. Past Chats provides a searchable table of conversation history. **Available Information:** * **Date created**: When the conversation started * **Conversation title**: Name or topic of the conversation * **Version**: Agent version used for the conversation * **Started by**: User who initiated the conversation * **Last message**: Timestamp of the most recent message **Filtering & Search:** * Search conversations by content or title * Filter by time period (All time, Last 7 days, Last 30 days, etc.) * Sort by date created or last message timestamp Click any conversation to view the full message history, including agent responses and tool usage. ## Best Practices Begin with basic configuration, add complexity as needed Test agents with various scenarios before production Track metrics and optimize continuously Define resource and cost limits upfront Handle errors and edge cases gracefully Review conversation logs and update instructions Act on user feedback to improve agent performance Configure tools and capabilities for your agents # Chat & Widget Source: https://docs.noxus.ai/platform/agents/deployments/chat-and-widget Deploy your agent as an authenticated in-platform chat or as an embeddable widget on your website The **Chat** and **Widget** deployments are Noxus-hosted interfaces — they need no third-party credentials and ship with a built-in conversation UI. * **Chat** — an authenticated web chat available inside the Noxus platform (or at a deployment URL) for users in your workspace. * **Widget** — an embeddable chat widget you can drop onto any website, letting external visitors talk to the agent. Both deployments share the same rendering layer, so the agent can send markdown, images, file attachments, buttons, and adaptive cards. They differ mainly in **who can access them** and in **how they're branded**. Both the Chat and the Widget include a built-in **conversation history** panel — users can pick up any of their previous conversations where they left off or start a brand-new one at any time. ## Chat Deployment Chat is the quickest way to put an agent in front of a user. No setup is needed beyond publishing the agent and choosing who is allowed to use it. Chat interface ### Deployment | Field | Description | | :---------- | :---------------------------------------------------------------------------------------------------- | | **Status** | Toggle the deployment **Online** or **Offline**. A published version is required before going online. | | **Version** | The published agent version this deployment uses. | A published **version** is required before you can set a deployment to Online. Switch the deployment to **Offline** before editing its configuration. ### Access Control who in your workspace can open the chat. | Field | Description | | :-------------------------------------- | :------------------------------------------------------------------------------------------------------ | | **Custom Instructions** | Per-deployment instructions appended to the agent's system prompt (e.g. `"Always respond in Spanish"`). | | **Allow all app users to use this app** | When enabled, every **App user** in the workspace can open the chat. | | **Allowed users** | When the toggle above is off, pick a specific list of users who can access this deployment. | ### Branding Override the default Noxus look and feel to match your brand. | Field | Description | | :---------------------- | :----------------------------------------------------------------------------------------- | | **Use custom branding** | Master toggle — when off, the chat uses the default theme and the fields below are hidden. | | **Agent logo** | Image shown next to the agent name in the chat header. | | **Sidebar logo** | Image shown at the top of the sidebar. Falls back to your tenant logo when empty. | | **Main color** | Primary / accent color set. Pick from presets or provide a custom color. | Pick **fairly light** colors — chat text is rendered in black, so dark backgrounds make messages hard to read. When you edit the **main color**, the system automatically derives a matching **accent color**; you can override it afterwards if you'd prefer a different pairing. *** ## Widget Deployment The Widget is an embeddable chat that can live on any public website. It shares the agent's configuration with the rest of the platform, but exposes a public front-end that anonymous visitors can use. Widget chat and conversation history ### Setting Up the Widget On the agent's **Deployments** page, create a new **Widget** deployment and publish a version. Once the deployment is Online, Noxus generates an embed snippet you can paste into your website's HTML. Widget embed snippet Add the snippet to any page where the widget should appear. Visitors will see the chat bubble in the corner and can start a conversation without signing in. ### Deployment | Field | Description | | :---------- | :------------------------------------------------ | | **Status** | Toggle the deployment **Online** or **Offline**. | | **Version** | The published agent version this deployment uses. | ### Widget content Copy shown to visitors in the widget UI. Every field is optional — leave blank to use the defaults. | Field | Description | | :---------------------------------- | :----------------------------------------------------------------- | | **Widget title** | Shown at the top of the widget panel. | | **Widget description** | Short subtitle shown under the title. | | **Custom Instructions** | Per-deployment instructions appended to the agent's system prompt. | | **Title** | Title shown on the **conversations history** page. | | **Subtitle** | Subtitle shown on the conversations history page. | | **New conversation button** | Label used for the "start a new conversation" CTA. | | **No conversations message** | Placeholder text shown when the visitor has no conversations yet. | | **Title logo** | Optional square image shown next to the widget title. | | **Show "Powered by Noxus" message** | Toggle the Noxus credit line in the widget footer. | ### Branding | Field | Description | | :---------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------- | | **Use custom branding** | Master toggle for the branding fields below. | | **Bubble logo** | SVG code for the icon shown inside the chat bubble on your site. We recommend a 24×24px icon — the SVG is rendered with the theme's colors and sizing. | | **Main color** | Primary / accent color set used throughout the widget. Pick from presets or provide a custom color. | Because the widget is **public**, keep Custom Instructions focused on behavior (tone, scope, fallback responses). Avoid putting secrets or internal URLs there — anything in the agent's prompt can be surfaced to visitors through conversation. ### Access & security By default the widget is fully public: it can be embedded on any site and anyone who loads it can chat. Two independent, opt-in controls let you lock that down. #### Domain locking Restrict which websites are allowed to embed the widget. When enabled, Noxus emits a `Content-Security-Policy: frame-ancestors` header so browsers refuse to render the widget anywhere else. | Field | Description | | :---------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------- | | **Restrict embedding to allowed domains** | When on, only the domains you list can embed the widget. When off, it can be embedded anywhere. | | **Allowed domains** | Origins permitted to embed the widget, e.g. `https://example.com` or `https://*.example.com` (wildcard subdomains). One per entry. | #### Authentication | Field | Description | | :---------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Authentication** | **Public** — anyone who can load the widget can chat. **Signed token** — your site's backend mints a signed token for each user; calls without a valid token are rejected. **Noxus user** — the agent runs as the visitor's real Noxus account, authenticated by a Noxus-issued JWT (see below). | | **Token signing algorithm** | **HS256** — a shared secret your backend signs with (stored encrypted by Noxus). **RS256** — your backend signs with a private key; Noxus verifies with your public key / JWKS, so no secret is stored. | | **Signing secret** *(HS256)* | The shared secret used to verify tokens. Leave blank when editing to keep the current value. | | **JWKS URL** / **Public key (PEM)** *(RS256)* | Where Noxus fetches your verification key. | | **Token audience (`aud`)** / **Token issuer (`iss`)** | Optional expected claims. Setting `aud` is strongly recommended in production to isolate this widget's tokens from other services that share the signing key. | | **Visitor identity** | **Token claim** *(default)* — the claim below identifies the visitor and their conversations are keyed to it. **None** — no identity claim is required; each sign-in gets its own isolated history. Available only with **Session** token usage, which is where the per-session identity is minted. | | **Identity claim** | Which claim carries the visitor's stable id, as a dot path (e.g. `user.id`). Blank uses the standard `sub`. A token carrying no id at this claim is rejected, in both token usages — conversations are keyed to this value. | In **Signed token** mode the visitor's chat is bound to the token's identity claim (`sub` by default), so users can only see their own conversations. Always set the **Allowed domains** so a token can't be replayed from an unapproved site. #### Host page integration (signed token) When **Signed token** mode is on, your embedding page supplies a token that Noxus validates on every request. Define a provider function before loading the embed snippet: ```html theme={null} ``` A static `window.NOXUS_EMBED_TOKEN = ""` is also supported, but a provider function is preferred because it can return a fresh token when the previous one expires. Your backend mints the JWT after authenticating the user. Required and optional claims: | Claim | Required | Description | | :------------ | :------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `exp` | Yes | Expiry. Keep it short-lived. | | `sub` | Yes | Stable per-user id — conversations are keyed to it. Point **Identity claim** at another claim (`user.id`, `user_id`, …) if your token carries the id elsewhere, or, on **Session** token usage, set **Visitor identity** to **None** if it carries no id at all. | | `origin` | When domain locking is on | The embedding page's origin (e.g. `https://example.com`); must match an allowed domain. | | `aud` / `iss` | Optional | Must match the configured audience / issuer when set. | The token is delivered to the widget over `postMessage` and sent to Noxus in an `x-embed-token` header — it never appears in the page URL. #### Token usage: per request or session **Token usage** decides what your token is *for*. The two settings are mutually exclusive — each accepts exactly one kind of credential, so there is no fallback between them. | Setting | How it works | Revocation | | :---------------------- | :---------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------- | | **Session** *(default)* | Your token is exchanged **once** for a Noxus session; the widget then uses a short-lived Noxus token. | Your token expiring no longer ends access — see the warning below. | | **Per request** | Your token is sent with, and verified on, every call. | Stop minting tokens and access ends within one token expiry. | In **Session** mode, revoking a user on your side does not end their chat immediately. It ends at the next rollover — the idle timeout or maximum length, whichever comes first — because that is when the widget asks your token provider again. To cut someone off before then, call the revoke endpoint below. Existing widgets were moved to **Session** automatically. If you rely on your own token expiry to end access, switch the deployment to **Per request**. Session mode also means your token-minting service being briefly unavailable no longer interrupts a live conversation — it is only consulted when a session ends, rather than every time a token expires. | Field | Description | | :--------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Idle timeout (minutes)** | Ends the session after this long without activity (5–1440). | | **Maximum session length (hours)** | Ends the session this long after sign-in regardless of activity (1–720). This is the longest a user you have stopped issuing tokens for can keep talking to the agent. | Neither deadline logs the visitor out by itself. When a session ends, the widget calls `NOXUS_EMBED_TOKEN_PROVIDER` for a newly minted token and starts a fresh session with it — the token it was holding is never reused for this. So these two settings control **how often your provider is asked again**, which is what makes them the bound on revocation: a visitor you still vouch for never notices the rollover happened, and one you have stopped issuing tokens for is signed out at the next one. Nothing changes on your embedding page: you still supply `NOXUS_EMBED_TOKEN_PROVIDER`, and the widget simply calls it once at the start of a session rather than on every expiry. That is why the switch was safe to apply to existing widgets — no host-page change is needed. Conversation history is unaffected — sessions are still keyed to your token's identity claim, so switching a live widget between the two settings keeps existing conversations. **Widget not loading?** Check the browser console for `session exchange failed (401)` and the response body for `no subject found at claim 'sub'`. Every signed-token widget needs an identity for the visitor, because conversations are keyed to it. Point **Identity claim** at the claim your token actually carries — an email claim works fine as an id — or, on **Session** token usage, set **Visitor identity** to **None** to have Noxus mint a per-session identity instead. Tokens that carry no id but do map to a Noxus user through **Email claim** are unaffected: those visitors are identified by the account they map to. ##### Revoking a user's sessions Mint a token for the user you want to cut off (the same signing key and claims as usual) and post it to the revoke endpoint. Every live session for that identity ends immediately. Not available when **Visitor identity** is **None** — there is no per-visitor id to revoke by, so bring the widget offline instead. ```http theme={null} POST /api/public/embed/session/revoke Content-Type: application/json x-embed-token: { "deployment_id": "YOUR_DEPLOYMENT_ID" } ``` Returns `{ "revoked": }`. Ending the live sessions is only half of it: the widget answers a dead session by starting a new one, so a revoked visitor still holding the token they were revoked with would simply be issued another. Noxus therefore also refuses to open a new session for that identity until **you mint them a token again** — specifically, until it sees a token whose `iat` is later than the revocation. Your tokens must carry an `iat` claim for revocation to be reversible. A token without one cannot prove it was minted after the revocation, so it is refused, and a revoked visitor stays locked out until your minter starts issuing `iat`. The same applies after taking a widget offline, which revokes every live session on it. #### Authenticate as a Noxus user (`noxus_user`) Choose **Noxus user** when the agent should run as the visitor's *real Noxus account* — it can read and act on everything that user can in Noxus. Authentication is a **Noxus-issued JWT** your integration obtains and relays as a bearer token; it does **not** rely on browser cookies (third-party cookies are blocked inside cross-origin iframes, so cookie-based sessions aren't reliable here). Noxus is the authorizer: the user consents, Noxus mints an **ES256** access token (≈1h) signed with a platform keypair, and your page relays the access token to the widget. Each authorized client is revocable from **Settings → Authorized clients**. ##### Recommended: the Noxus consent popup (no backend, no CORS) For a website with no backend, open the Noxus-hosted consent popup and wait for the token — the whole flow runs on the Noxus origin, so your page never makes a cross-origin call: ```html theme={null} ``` Noxus validates the requesting `origin` against the deployment's **Allowed domains** and delivers the token via `postMessage` scoped to that origin, so it can only reach the page you authorized. When the token expires, the provider is called again and re-opens the popup — silent if the Noxus session is still valid. ##### Alternative: server-side PKCE (browser extensions, host backends) If you have a backend (or you're a browser extension), run the PKCE flow and exchange the code server-side, then relay the access token the same way. This is the flow the Noxus Copilot extension uses. With the user signed in to Noxus, send them to the consent page with a PKCE `code_challenge` and your `redirect_uri`. On consent, Noxus registers a device and redirects back with a one-time `code`. ```http theme={null} POST /api/public/clients/token Content-Type: application/json { "grant_type": "authorization_code", "code": "", "code_verifier": "" } ``` Returns `access_token`, `refresh_token`, and `expires_in`. ```html theme={null} ``` The widget sends it as `Authorization: Bearer`; Noxus verifies the signature, checks the device is still active, and runs the conversation as that user. ```http theme={null} POST /api/public/clients/token Content-Type: application/json { "grant_type": "refresh_token", "refresh_token": "" } ``` Because the provider is called on demand, returning a refreshed token from it keeps the session alive with no reload. Revoke a device at any time — this invalidates its access and refresh tokens immediately: ```http theme={null} POST /api/backend/clients/{client_id}/revoke POST /api/backend/clients/revoke-all ``` Domain locking is **recommended** for `noxus_user` but not required: a forged token fails signature verification server-side no matter where the widget is framed, so the JWT — not the frame origin — is the security boundary. ### Page context A host page (e.g. a browser extension or your own app) can feed the agent **what the user is currently looking at**, so it can answer with that context in mind — the record open in your CRM, the article being read, the cart contents, etc. 1. Enable **Accept page context from the embedding page** in the widget deployment's **Access & security** settings. It's off by default; only turn it on for embeds you trust, since the context is client-supplied. 2. From the host page, push context whenever it changes: ```js theme={null} // Using the embed.js script: window.SpotChatWidget.setContext({ objectType: "Case", recordId: "500X", title: "Login fails on SSO" }); // Pass null to clear it: window.SpotChatWidget.setContext(null); // Or, if you embed the iframe yourself, post the message directly: iframe.contentWindow.postMessage( { type: "SPOT_WIDGET_CONTEXT", context: { /* any JSON or string */ } }, "*" ); ``` The context is attached to each message the user sends and injected into that turn only — it is **never** stored in conversation history. It is treated as untrusted reference data (not instructions) and is capped at 8,000 characters. A string is used verbatim; any other value is JSON-encoded. # Google Chat Source: https://docs.noxus.ai/platform/agents/deployments/google-chat Deploy your agent to Google Chat spaces and direct messages Deploy your agent to Google Chat and let users interact with it directly from any space, group chat, or 1:1 direct message. Once activated, Noxus invites the bot to the configured space and routes incoming messages to your agent. ## Choosing an event delivery mode Google Chat can deliver events to Noxus in two ways. Pick one **before** you configure the deployment. | Mode | When to use it | | :---------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **HTTPS webhook** *(default)* | Your Noxus relay has a public URL Google Chat can POST to. Simpler to set up, no Google Cloud IAM work. | | **Cloud Pub/Sub** | Your Noxus relay can't expose a public endpoint (on-prem, locked-down VPC, firewalled deployment). The customer's Google Cloud project owns the topic; Noxus pulls events with service-account credentials. | The choice is exposed as the **Event delivery** dropdown in the deployment's **Conversation triggers** section. Switching modes requires deactivating and reactivating the deployment. ## Setting Up the Google Chat Deployment There are two ways to authenticate the bot: as a **workspace user** via OAuth (the Noxus-managed Google account flow) or as an **app-style bot** via a Google Cloud **service account**. Pick the option that matches how you want the bot to appear in Chat. **Cloud Pub/Sub delivery requires the service-account path (Option B).** OAuth user tokens don't pair with Pub/Sub IAM and Noxus will reject activation. If you need Pub/Sub delivery, use a service account. ### Option A: Connect with a Google Workspace user (OAuth) The simplest path. The bot acts on behalf of the connected user — messages it sends are attributed to that user and it can only post in spaces the user has access to. In Noxus, open **Settings → Integrations**, find **Google Chat**, and click **Connect**. Choose **Google Chat (Workspace user)** as the provider and complete the Google sign-in. Grant the requested Chat scopes: read messages, send messages, react, and manage memberships. Navigate to your agent's **Deployments** page and create a new Google Chat deployment. Pick the connection you just authorised. Open the target Chat space in your browser. The URL contains the space ID — copy the resource name in the form `spaces/AAAAxxxxxxx`. Paste it into **Space resource name** in the deployment configuration. Set the deployment **Online**. Noxus calls the Chat API to add the bot to the space. The bot is now ready to receive messages. The bot only sees messages in spaces it's been invited to. If activation fails with a permissions error, make sure the connected user has at least the Chat **Member** role in the target space. ### Option B: Connect with a service account (app-style bot) Use this option when you want a dedicated bot identity (separate from any human user) — the recommended path for production deployments and required for **domain-wide delegation**. In the [Google Cloud Console](https://console.cloud.google.com), enable the **Google Chat API** for your project. Open **Chat API → Configuration** and configure your Chat app — give it a name, avatar, and description. Note the **Project number** (not the project ID). Still in **Chat API → Configuration**, under **Connection settings**, choose **HTTP endpoint URL** and paste the webhook URL shown in the deployment configuration: ``` https://relay.your-noxus-domain.com/webhooks/ ``` The exact URL is shown in the **Webhook URL** banner on the deployment screen — copy that one. In Google Cloud Console, open **IAM & Admin → Service Accounts**. Create a new service account (or pick an existing one) and grant it the Chat scopes: * `https://www.googleapis.com/auth/chat.bot` * `https://www.googleapis.com/auth/chat.messages` * `https://www.googleapis.com/auth/chat.messages.reactions` * `https://www.googleapis.com/auth/chat.spaces` * `https://www.googleapis.com/auth/chat.memberships` Then click **Keys → Add Key → Create new key → JSON** to download a JSON key file. In Noxus, open the Google Chat connection dialog and choose **API Credentials**: * **Service account JSON** — paste the entire contents of the JSON key file * **Impersonation subject** *(optional)* — only set this if you've configured **domain-wide delegation** in Google Workspace and want the bot to act on behalf of a specific user (e.g. `bot-runner@yourdomain.com`) Click **Connect** — Noxus mints a token and calls the Chat API to verify everything is wired up. Open the target Chat space and copy its resource name (`spaces/AAAAxxxxxxx`). Paste it into **Space resource name** on the deployment, then set the deployment **Online**. Noxus invites the bot to the space automatically. The same webhook URL handles every Google Chat deployment in your workspace. Noxus verifies every inbound event's JWT against this exact URL (the audience Google signs into the token), so make sure the URL pasted into Cloud Console matches the **Webhook URL** banner exactly — including scheme and any path prefix. ### Option C: Cloud Pub/Sub delivery Use this when the Noxus relay can't accept inbound HTTPS — for example, on-prem deployments or VPC-locked environments. Instead of Chat POSTing events to a URL, Chat publishes them to a **Cloud Pub/Sub topic** in your Google Cloud project. Noxus pulls from a subscription on that topic with service-account credentials. This mode reuses the **service-account** Chat connection from Option B above (Pub/Sub delivery is not compatible with OAuth-user credentials). In Google Cloud Console, open **Pub/Sub → Topics** in the **same project that owns the Chat app**. Create a topic — for example, `chat`. The full topic name will be `projects/YOUR_PROJECT/topics/chat`. Create a **pull** subscription on that topic — for example, `chat-sub`, with name `projects/YOUR_PROJECT/subscriptions/chat-sub`. Defaults are fine; no push endpoint is needed. Google Chat publishes events from a Google-managed service agent. The agent's email is shown on the **Chat API → Configuration** page under **Service Account Email**, in the form: ``` service-@gcp-sa-gsuiteaddons.iam.gserviceaccount.com ``` On the **topic** (not the subscription), grant this email the **`roles/pubsub.publisher`** role. ```bash theme={null} gcloud pubsub topics add-iam-policy-binding chat \ --project=YOUR_PROJECT \ --member=serviceAccount:service-@gcp-sa-gsuiteaddons.iam.gserviceaccount.com \ --role=roles/pubsub.publisher ``` Without this grant **Google Chat silently drops every event** — the Chat API UI shows no error, but no message ever reaches the subscription. Verify by pulling the subscription directly (`gcloud pubsub subscriptions pull projects/YOUR_PROJECT/subscriptions/chat-sub --auto-ack`) after sending a test message; if it's empty, the publisher role is missing. The same service account JSON you uploaded to Noxus (Option B, step 3) is what Noxus uses to pull from the subscription. On the **subscription** (not the topic), grant that SA the **`roles/pubsub.subscriber`** role. ```bash theme={null} gcloud pubsub subscriptions add-iam-policy-binding chat-sub \ --project=YOUR_PROJECT \ --member=serviceAccount:@.iam.gserviceaccount.com \ --role=roles/pubsub.subscriber ``` Without this grant, Noxus's pre-flight check fails at activation with a precise error message telling you which SA and which subscription need the role. In **Chat API → Configuration → Connection settings**, choose **Cloud Pub/Sub** and paste the topic name (e.g. `projects/YOUR_PROJECT/topics/chat`). Save. On the agent's Google Chat deployment in Noxus, set **Event delivery** to **Cloud Pub/Sub** and paste the subscription name (`projects/YOUR_PROJECT/subscriptions/chat-sub`) into the **Pub/Sub subscription** field. Set the deployment **Online**. Noxus runs a one-shot Pub/Sub pull as part of activation to confirm the subscriber role is in place — if either IAM grant is missing, activation fails with an actionable error before the deployment goes live. **Two IAM grants, two different principals, two different resources** — these are the most common point of confusion: | Direction | Principal | Resource | Role | | :------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------- | :------------------------ | | Google Chat → topic | `service-@gcp-sa-gsuiteaddons.iam.gserviceaccount.com` (Google's Chat-side service agent, shown in **Chat API → Configuration**) | The Pub/Sub **topic** | `roles/pubsub.publisher` | | Subscription → Noxus | Your own service account (the one in the JSON key uploaded to Noxus) | The Pub/Sub **subscription** | `roles/pubsub.subscriber` | Pub/Sub deployments listen on **every space the Chat app is added to**, not just one. The **Space resource name** and **Direct messages only** fields become content filters in this mode — leave them blank and the bot responds in any space it's invited to. Set them only if you want to ignore events from other spaces. ## Deployment Settings Once your Google Chat connection is established, configure how the agent behaves. Settings are organised into the sections below. ### Deployment | Field | Description | | :---------- | :---------------------------------------------------------------------------------------------------------------------------- | | **Status** | Toggle the deployment **Online** or **Offline**. Going Online adds the bot to the configured space; going Offline removes it. | | **Version** | The published agent version this deployment uses. | A published **version** is required before you can set a deployment to Online. Switch to **Offline** to edit configuration. ### Configuration | Field | Description | | :------------------------- | :------------------------------------------------------------------------------------------------------------------------------ | | **Google Chat Connection** | The Google Chat connection to use (OAuth user or service account). Manage connections in your workspace's connections settings. | | **Custom Instructions** | Per-deployment instructions appended to the agent's system prompt (e.g. `"Always respond in Spanish"`). | ### Conversation Triggers A new conversation starts when a user posts in the configured space. You can optionally restrict to direct messages or filter by keyword. | Field | Description | | :----------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Direct messages only** | When enabled, the bot listens to **every** direct message it receives in this workspace and ignores events from rooms or group chats. Turning this on hides the space resource name field — DM spaces are created on demand by Google Chat the first time a user messages the bot, so there's no single space to pin. Useful for personal-assistant style deployments. | | **Space resource name** | *(Hidden when "Direct messages only" is on.)* The Chat space resource name (e.g. `spaces/AAAAxxxxxxx`). The bot is added to this space on activation. | | **Trigger keywords** | One or more keywords that must appear in the message (e.g. `/start`, `help`). Leave empty to respond to every message. | Keyword filtering only gates **conversation creation**. Once a conversation is active, follow-up messages don't need to repeat the keyword. Only one **Direct messages only** deployment should exist per workspace at a time. Multiple DM-only deployments in the same workspace will all fire on every incoming DM, which is rarely what you want. ### Reply Behavior | Field | Description | | :--------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------- | | **Reply mode** | `Auto` — reply in the same context. `Thread` — always reply in a thread. `Tool-based` — agent decides when and where to send messages via tools. | | **Batch messages before replying** | When enabled, the agent waits for a pause before responding, batching multiple incoming messages together. | | **Quiet period (seconds)** | How long to wait after the last message before replying. New messages reset the timer. Default: `5`. | ### Channel Tools Toggle which Google Chat-specific tools the agent can use during conversations. | Tool | Description | | :------------------- | :--------------------------------------- | | **Send Message** | Send messages to the Chat space. | | **React To Message** | Add Unicode emoji reactions to messages. | | **Reply In Thread** | Reply to messages in a thread. | ### Rate Limits | Field | Description | | :--------------------------------- | :------------------------------------------------------------------------------------------------------- | | **Max messages per minute** | Limit how many messages the agent sends per minute. `0` for unlimited. | | **Max new conversations per hour** | Limit how many new conversations can be started per hour. `0` for unlimited. | | **Conversation timeout (minutes)** | After this many minutes of inactivity, the next message starts a fresh conversation. `0` for no timeout. | ### Display | Field | Description | | :------------------ | :-------------------------------------------------------------------- | | **Show thinking** | Display the agent's reasoning steps in Chat messages. Off by default. | | **Show tool calls** | Display tool call status in Chat messages. Off by default. | | **Show sources** | Display source citations in Chat messages. On by default. | ## Troubleshooting The connected credential lacks the `chat.memberships` scope. For OAuth, reconnect and grant the **Manage memberships** permission. For a service account, add the `https://www.googleapis.com/auth/chat.memberships` scope to the SA and either re-grant domain-wide delegation or invite the bot to the space manually. Two things to check: 1. The URL pasted into Cloud Console (**Chat API → Configuration → Connection settings → HTTP endpoint URL**) matches the **Webhook URL** banner on the deployment **exactly** — same scheme, host, and path. Google signs the JWT with that URL as the audience, and Noxus verifies the audience against `RELAY_URL + /webhooks/`. A mismatch logs `[GCHAT] JWT rejected (expected_aud=...)` in the relay and returns 401. 2. The bot is **invited to the space**. For service-account auth this happens automatically at activation. For OAuth-user auth, the user must be a member of the target space. Almost always a missing scope on the connection. The conversation runs successfully on the Noxus side, but `chat.googleapis.com/v1/{space}/messages` returns `403`. Add the `chat.messages` scope (OAuth) or grant the corresponding scope to the service account, then reconnect. Check the **Direct messages only** toggle in **Conversation Triggers**. When enabled the bot drops every event whose space type isn't `DIRECT_MESSAGE`. Disable it to respond in spaces and rooms as well. Pub/Sub-mode activation runs a one-shot pull against the configured subscription to surface IAM problems before the deployment goes live. Two common cases: * **`PermissionDenied … pubsub.subscriptions.consume`** — the service account whose JSON key you uploaded to Noxus is missing **`roles/pubsub.subscriber`** on the subscription. Grant the role on the **subscription** (not the topic). The error message includes the exact SA email and subscription path to grant against. * **`NotFound … subscription`** — the subscription resource name is wrong, or it exists in a different Google Cloud project than the one in the path. Double-check the project ID and subscription name. If activation succeeded but the bot never responds, Chat is failing to publish events. The Chat API UI gives no feedback when this happens — events are dropped silently. Confirm in this order: 1. **Publisher role.** On the topic, grant `roles/pubsub.publisher` to the Google-side Chat service agent (`service-@gcp-sa-gsuiteaddons.iam.gserviceaccount.com`, shown in **Chat API → Configuration → Service Account Email**). This is the most common cause. 2. **Verify the pipe with `gcloud`.** Pull the subscription directly while sending a test message: ```bash theme={null} gcloud pubsub subscriptions pull projects/YOUR_PROJECT/subscriptions/chat-sub \ --project=YOUR_PROJECT --limit=5 --auto-ack ``` If the pull returns no messages, Chat is the problem (recheck the publisher role and the topic name in **Chat API → Configuration**). If the pull returns messages but Noxus isn't reacting, check the deployment's **Events** tab — runtime failures (e.g. a revoked subscriber role) surface there with the exact exception class and message. 3. **Bot membership.** Pub/Sub mode receives events for every space the Chat app is in — but Chat only generates events for spaces where the bot has been invited or for DMs that users have initiated. Start a DM with the bot (or `@mention` it in a space) so Chat has something to publish. # Slack Source: https://docs.noxus.ai/platform/agents/deployments/slack Deploy your agent to Slack and let users interact with it directly in channels and DMs Deploy your agent to Slack and let users interact with it directly from any Slack channel or direct message. Once deployed, users can mention the bot or message it directly to start conversations powered by your agent. ## Setting Up the Slack Deployment There are two ways to connect your agent to Slack: using the **Noxus-managed Slack bot** or connecting your **own custom Slack app**. ### Option A: Use the Noxus-Managed Bot The quickest way to get started is to install the Noxus bot directly into your Slack workspace. In the Noxus platform, navigate to your agent's **Deployments** page and create a new Slack deployment. You will see a notice to invite the Noxus Bot to your workspace: Add Noxus Bot to Slack Click **[Add it to your Slack here](https://noxusai.slack.com/oauth?client_id=4411224265729.6929759701985\&scope=app_mentions%3Aread%2Cchannels%3Ahistory%2Cchannels%3Aread%2Cchat%3Awrite%2Ccommands%2Cfiles%3Aread%2Cfiles%3Awrite%2Cgroups%3Ahistory%2Cgroups%3Aread%2Cim%3Ahistory%2Cim%3Aread%2Cim%3Awrite%2Clinks%3Aread%2Cmpim%3Ahistory%2Cusers%3Aread\&user_scope=\&redirect_uri=\&state=\&granular_bot_scope=1\&single_channel=0\&install_redirect=\&tracked=1\&user_default=0\&team=)** to authorize the bot in your Slack workspace. Once authorized, invite the bot to any channel where you want it to respond by typing `/invite @Noxus Bot` in that channel. Invite the Noxus Bot to every Slack channel where you want it to respond. The bot will only see messages in channels it has been added to. ### Option B: Connect Your Own Slack App If you need more control over permissions or branding, you can connect your own Slack app instead. Here we are connecting an app named "Bug Reporter". Go to [api.slack.com/apps](https://api.slack.com/apps) and either create a new app or select an existing one. In your Slack app's settings, go to **Basic Information** to find your **App ID** and **Signing Secret**: Slack Basic Information — App ID and Signing Secret Go to **OAuth & Permissions** in the sidebar and copy the **Bot User OAuth Token** (starts with `xoxb-`): Slack OAuth & Permissions — Bot User OAuth Token Back in Noxus, open the Slack connection dialog and fill in the three fields: * **Bot User OAuth Token** — the `xoxb-...` token from OAuth & Permissions * **Signing Secret** — from Basic Information → App Credentials * **App ID** — from Basic Information → App Credentials Noxus Slack connection dialog Click **Connect** to save the connection. After creating your Slack deployment, Noxus will generate a **Webhook URL**. Copy it: Noxus Webhook URL for Slack Go to your Slack app's **Event Subscriptions** page, enable events, and paste the URL into the **Request URL** field. Still on the Event Subscriptions page, scroll down to **Subscribe to bot events** and add the following events: * `message.channels` — messages in public channels * `message.groups` — messages in private channels * `message.im` — direct messages * `message.mpim` — group direct messages Slack Event Subscriptions — bot events Click **Save Changes** at the bottom of the page. After updating event subscriptions, make sure Slack verifies your Request URL successfully (you should see a green checkmark). If verification fails, double-check that the Webhook URL was copied correctly. ## Deployment Settings Once your Slack connection is established, configure how the agent behaves in Slack. Settings are organized into the sections below. Slack deployment settings ### Deployment | Field | Description | | :---------- | :---------------------------------------------------------------------------------------------------------------------------- | | **Status** | Toggle the deployment **Online** or **Offline**. When online, the agent listens and responds to Slack messages automatically. | | **Version** | The published agent version this deployment uses. Each version is a snapshot of prompts, tools, and model settings. | A published **version** is required before you can set a deployment to Online. You cannot edit the configuration while the deployment is active — switch it to **Offline** first, then make your changes. ### Configuration | Field | Description | | :---------------------- | :------------------------------------------------------------------------------------------------------ | | **Slack Connection** | The Slack workspace connection to use. Manage connections in your workspace's connections settings. | | **Custom Instructions** | Per-deployment instructions appended to the agent's system prompt (e.g. `"Always respond in Spanish"`). | ### Conversation Triggers A new conversation starts when **all** specified fields match an incoming message. The fields use AND logic — every non-empty field must match for the trigger to fire. | Field | Description | | :---------- | :---------------------------------------------------- | | **Mention** | Slack user the bot must be mentioned by or as. | | **Channel** | Restrict triggers to a specific channel. | | **Keyword** | One or more keywords that must appear in the message. | Leave a field empty to match any value. For example, setting only **Channel** will trigger on every message in that channel regardless of mention or keyword. ### Reply Behavior | Field | Description | | :--------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------- | | **Reply mode** | `Auto` — reply in the same context. `Thread` — always reply in a thread. `Tool-based` — agent decides when and where to send messages via tools. | | **Batch messages before replying** | When enabled, the agent waits for a pause before responding, batching multiple incoming messages together. | | **Quiet period (seconds)** | How long to wait after the last message before replying. New messages reset the timer. Default: `5`. | ### Channel Tools Toggle which Slack-specific tools the agent can use during conversations. | Tool | Description | | :------------------------- | :------------------------------------------------------ | | **Send Message** | Send messages to the Slack channel. | | **React To Message** | Add emoji reactions to messages. | | **Reply In Thread** | Reply to messages in a thread. | | **Get User Profile** | Look up a Slack user's profile information. | | **Send Ephemeral Message** | Send a private message visible only to a specific user. | ### Rate Limits | Field | Description | | :--------------------------------- | :------------------------------------------------------------------------------------------------------- | | **Max messages per minute** | Limit how many messages the agent sends per minute. `0` for unlimited. | | **Max new conversations per hour** | Limit how many new conversations can be started per hour. `0` for unlimited. | | **Conversation timeout (minutes)** | After this many minutes of inactivity, the next message starts a fresh conversation. `0` for no timeout. | ### Display | Field | Description | | :------------------ | :--------------------------------------------------------------------- | | **Show thinking** | Display the agent's reasoning steps in Slack messages. Off by default. | | **Show tool calls** | Display tool call status in Slack messages. Off by default. | | **Show sources** | Display source citations in Slack messages. On by default. | ## Troubleshooting Make sure the **Signing Secret** entered in the Slack connection matches the one in **Basic Information → App Credentials** on api.slack.com. Noxus uses the signing secret to verify every incoming event; if it's wrong or stale, Slack requests fail signature verification and no events reach the deployment. The bot is missing the `chat:write` scope. The conversation runs successfully on the Noxus side, but posting back to Slack fails silently. Add `chat:write` under **OAuth & Permissions → Bot Token Scopes** in api.slack.com, reinstall the app to your workspace, and update the Bot User OAuth Token in the Noxus Slack connection. # Microsoft Teams Source: https://docs.noxus.ai/platform/agents/deployments/teams Deploy your agent to Microsoft Teams channels and private chats Deploy your agent to Microsoft Teams and let users talk to it in team channels or direct chats. Once deployed, the agent can respond to mentions, reply in threads, and even send rich Adaptive Cards right inside Teams. ## Setting Up the Teams Deployment Teams deployments require an **Azure AD (Entra ID)** application that the agent uses to read messages and post replies. You can either connect via delegated OAuth (signing in as a user) or via an **app-only service principal** (recommended for production and DMs). Go to [portal.azure.com](https://portal.azure.com) → **Microsoft Entra ID** → **App registrations** → **New registration**. Give the app a name (e.g. `Noxus Agent`) and register it. Under **API permissions**, add the Microsoft Graph permissions the agent needs to read messages and post replies (for example `Chat.Create`, `ChatMessage.Read.All`, `Channel.ReadBasic.All`, `ChannelMessage.Send`, `Team.ReadBasic.All`, `User.Read.All`). A tenant admin must **Grant admin consent** for the application-level permissions. Under **Certificates & secrets**, create a new **Client secret** and copy the value — you will only see it once. Back in Noxus, create a new Microsoft Teams connection and fill in the fields: * **Tenant ID** — your Azure AD tenant UUID. * **Client ID (Application ID)** — from the app's **Overview** page. * **Client Secret** — the value you copied in the previous step. * **User ID or Email (UPN)** — optional. Leave empty for channel-only deployments; required when you want the agent to handle direct messages (DMs) with app-only permissions. Click **Connect** to save. Noxus validates the credentials against Microsoft Graph. Install the Noxus bot app into your Teams tenant (or add the bot to specific channels) so it can receive messages. The bot needs to be present in every channel where you want it to respond. Admin consent is required for channel-wide message subscriptions and DM subscriptions via app-only permissions. If your deployment can't activate, check that admin consent was granted for every Microsoft Graph permission. ## Deployment Settings Once your Teams connection is established, configure how the agent behaves in Teams. Settings are organized into the sections below. ### Deployment | Field | Description | | :---------- | :---------------------------------------------------------------------------------------------------------------------------- | | **Status** | Toggle the deployment **Online** or **Offline**. When online, the agent listens and responds to Teams messages automatically. | | **Version** | The published agent version this deployment uses. | A published **version** is required before you can set a deployment to Online. You cannot edit the configuration while the deployment is active — switch it to **Offline** first, then make your changes. ### Configuration | Field | Description | | :---------------------- | :------------------------------------------------------------------------------------------------------ | | **Teams Connection** | The Microsoft Teams connection to use. Manage connections in your workspace's connections settings. | | **Custom Instructions** | Per-deployment instructions appended to the agent's system prompt (e.g. `"Always respond in Spanish"`). | ### Conversation Triggers A new conversation starts when **all** specified fields match an incoming message. Fields use AND logic — every non-empty field must match. | Field | Description | | :-------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------- | | **Only support private messages** | When enabled, the agent only responds to direct chats (DMs) and is not bound to a team or channel. Useful for DM-based assistants. | | **Team** | The Team the agent listens to. Hidden when **Only support private messages** is on. | | **Channel** | The specific channel inside the Team where triggers fire. | | **Keywords** | One or more keywords that must appear in the message. | | **Reset conversation keywords** | Keywords that force a new conversation. If left empty, all private messages continue in the same conversation thread. | | **Trigger on my own messages** | Advanced — when on, the agent also fires on messages sent by the authenticated user. Off by default to prevent self-loops. | ### Reply Behavior | Field | Description | | :--------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **Reply mode** | `Auto` — reply in the same context (channel or DM). `Thread` — always reply in a thread. `Tool-based` — nothing is sent automatically; the agent must call the `send_message` tool to post. | | **Batch messages before replying** | When enabled, the agent waits for a pause before responding, batching multiple incoming messages together. | | **Quiet period (seconds)** | How long to wait after the last message before replying. New messages reset the timer. Default: `5`. | ### Channel Tools Toggle which Teams-specific tools the agent can use during conversations. | Tool | Description | | :------------------- | :--------------------------------------------------------------------------------------------------------- | | **React To Message** | Add an emoji reaction to a message (Teams supports `like`, `heart`, `laugh`, `surprised`, `sad`, `angry`). | | **Reply In Thread** | Reply to a specific message in a thread. | ### Rate Limits | Field | Description | | :--------------------------------- | :------------------------------------------------------------------------------------------------------- | | **Max messages per minute** | Limit how many messages the agent sends per minute. `0` for unlimited. | | **Max new conversations per hour** | Limit how many new conversations can be started per hour. `0` for unlimited. | | **Conversation timeout (minutes)** | After this many minutes of inactivity, the next message starts a fresh conversation. `0` for no timeout. | ### Display | Field | Description | | :------------------ | :--------------------------------------------------------------------- | | **Show thinking** | Display the agent's reasoning steps in Teams messages. Off by default. | | **Show tool calls** | Display tool call status in Teams messages. Off by default. | | **Show sources** | Display source citations in Teams messages. On by default. | # Telegram Source: https://docs.noxus.ai/platform/agents/deployments/telegram Deploy your agent as a Telegram bot with a one-step bot-token setup Deploy your agent as a Telegram bot in minutes. Once you paste the bot token, Noxus registers the webhook with Telegram automatically — no external configuration required. ## Setting Up the Telegram Deployment Telegram deployments use the standard **Telegram Bot API**. All you need is a bot token from [@BotFather](https://t.me/BotFather). Open Telegram and start a chat with [@BotFather](https://t.me/BotFather). Send `/newbot`, follow the prompts to choose a name and a username, and copy the bot token. It looks like `123456789:ABCdefGHIjklMNOpqrsTUVwxyz`. Creating a Telegram bot via BotFather In Noxus, create a new Telegram connection and paste the bot token. Noxus Telegram connection dialog Click **Connect** — Noxus calls `getMe` on the Telegram API to verify the token and record your bot's metadata. Create a Telegram deployment, pick this connection, and set it to **Online**. Noxus automatically registers the webhook with Telegram and secures it with a per-deployment secret token, so every incoming update is signed by Telegram. Open Telegram, search for your bot by its username, and send it a message. The agent will reply according to the deployment settings below. Talking to the Noxus bot on Telegram You don't need to configure a webhook URL on Telegram's side — Noxus handles registration when the deployment goes Online and removes the webhook when it goes Offline. ## Deployment Settings Once your Telegram connection is established, configure how the agent behaves. Settings are organized into the sections below. Telegram deployment settings ### Deployment | Field | Description | | :---------- | :--------------------------------------------------------------------------------------------------------------------------- | | **Status** | Toggle the deployment **Online** or **Offline**. Going Online registers the webhook with Telegram; going Offline deletes it. | | **Version** | The published agent version this deployment uses. | A published **version** is required before you can set a deployment to Online. Switch to **Offline** to edit configuration. ### Configuration | Field | Description | | :---------------------- | :------------------------------------------------------------------------------------------------------- | | **Telegram Connection** | The Telegram connection (bot token) to use. Manage connections in your workspace's connections settings. | | **Custom Instructions** | Per-deployment instructions appended to the agent's system prompt (e.g. `"Always respond in Spanish"`). | ### Conversation Triggers A new conversation starts when a user sends a message to the bot. You can optionally restrict this to messages containing specific keywords. | Field | Description | | :------------------- | :-------------------------------------------------------------------------------------------------------------------------------------- | | **Trigger keywords** | One or more keywords that must appear in the message (e.g. `/start`, `help`). Leave empty to respond to every message the bot receives. | ### Reply Behavior | Field | Description | | :------------- | :------------------------------------------------------------------------------------------------------------------------------------------------ | | **Reply mode** | `Auto` — reply directly to the user's message. `Tool-based` — nothing is sent automatically; the agent must call the `send_message` tool to post. | Telegram does not have threads, so the **Thread** reply mode and the **Batch messages** / **Quiet period** settings are not available on this channel. ### Channel Tools Toggle which Telegram-specific tools the agent can use during conversations. | Tool | Description | | :------------------- | :---------------------------------------------- | | **React To Message** | Add an emoji reaction to a message in the chat. | ### Rate Limits | Field | Description | | :--------------------------------- | :------------------------------------------------------------------------------------------------------- | | **Max messages per minute** | Limit how many messages the agent sends per minute. `0` for unlimited. | | **Max new conversations per hour** | Limit how many new conversations can be started per hour. `0` for unlimited. | | **Conversation timeout (minutes)** | After this many minutes of inactivity, the next message starts a fresh conversation. `0` for no timeout. | ### Display | Field | Description | | :------------------ | :------------------------------------------------------------------------ | | **Show thinking** | Display the agent's reasoning steps in Telegram messages. Off by default. | | **Show tool calls** | Display tool call status in Telegram messages. Off by default. | | **Show sources** | Display source citations in Telegram messages. On by default. | # WhatsApp Source: https://docs.noxus.ai/platform/agents/deployments/whatsapp Deploy your agent to WhatsApp Business and chat with customers on the world's largest messaging app Deploy your agent to WhatsApp via the **WhatsApp Business Cloud API** (Meta). Once connected, users can text the business phone number and the agent will reply — with text, media attachments, and typing indicators. ## Setting Up the WhatsApp Deployment WhatsApp deployments use Meta's Cloud API. You need a WhatsApp Business Account (WABA), a verified phone number, and a system-user access token with WhatsApp permissions. In [Meta Business Suite](https://business.facebook.com), create or select a Business Account and add **WhatsApp** to it. Register and verify a phone number — Noxus will send messages from this number. In your Meta Business account, open the WhatsApp configuration for your app and copy: * **Phone Number ID** — unique identifier for the registered business phone number. * **Business Account ID** — the WABA ID. Create a **system user** on Meta Business and generate a permanent access token with the `whatsapp_business_messaging` and `whatsapp_business_management` scopes. Copy the token. Choose any long random string — this is the **Webhook Verify Token**. You will give it to both Noxus and Meta so they can authenticate each other on incoming webhook requests. Back in Noxus, create a new WhatsApp connection and fill in the four fields: * **Phone Number ID** * **Business Account ID** * **Access Token** * **Webhook Verify Token** Click **Connect** — Noxus will call the Facebook Graph API (v22.0) to verify the phone number and the access token. On your WhatsApp configuration page in Meta, set the **Callback URL** to the webhook URL shown in Noxus and paste the same **Webhook Verify Token** you used above. Subscribe to the `messages` webhook field so the agent receives incoming messages. Your WhatsApp Business Account must be approved by Meta for production messaging. Until it is, WhatsApp caps you at a limited number of test recipients and test conversations. ## Deployment Settings Once your WhatsApp connection is established, configure how the agent behaves. Settings are organized into the sections below. ### Deployment | Field | Description | | :---------- | :--------------------------------------------------------------------------------------------------------------------------- | | **Status** | Toggle the deployment **Online** or **Offline**. When online, the agent replies to incoming WhatsApp messages automatically. | | **Version** | The published agent version this deployment uses. | A published **version** is required before you can set a deployment to Online. Switch to **Offline** to edit configuration. ### Configuration | Field | Description | | :---------------------- | :------------------------------------------------------------------------------------------------------------------------------------------- | | **WhatsApp Connection** | The WhatsApp connection to use. Manage connections in your workspace's connections settings. | | **Custom Instructions** | Per-deployment instructions appended to the agent's system prompt (e.g. `"Always respond in English"` or `"Never ask for payment details"`). | ### Conversation Triggers A new conversation starts when **all** specified fields match an incoming message. Leave a field empty to match any value. | Field | Description | | :------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Phone Number Filter** | Only respond to messages from this specific sender phone number (e.g. `+1234567890`). Leave empty to respond to everyone who messages your business number. | | **Keyword** | One or more keywords that must appear in the message. | | **Chunk messages** | When on, long agent responses are automatically split into smaller, easier-to-read WhatsApp messages. Recommended — WhatsApp has a strict 2,048-character per-message limit. | | **Show writing indicator** | When on, WhatsApp shows a "typing…" state while the agent composes a reply. | ### Reply Behavior | Field | Description | | :------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------- | | **Reply mode** | `Auto` — reply directly to the sender. `Tool-based` — nothing is sent automatically; the agent must explicitly call the `send_message` tool to reply. | WhatsApp does not support threads or reactions, so the **Thread** reply mode and the **Batch messages** / **Quiet period** settings are not available on this channel. ### Rate Limits | Field | Description | | :--------------------------------- | :------------------------------------------------------------------------------------------------------- | | **Max messages per minute** | Limit how many messages the agent sends per minute. `0` for unlimited. | | **Max new conversations per hour** | Limit how many new conversations can be started per hour. `0` for unlimited. | | **Conversation timeout (minutes)** | After this many minutes of inactivity, the next message starts a fresh conversation. `0` for no timeout. | ### Display | Field | Description | | :------------------ | :------------------------------------------------------------------------ | | **Show thinking** | Display the agent's reasoning steps in WhatsApp messages. Off by default. | | **Show tool calls** | Display tool call status in WhatsApp messages. Off by default. | | **Show sources** | Display source citations in WhatsApp messages. On by default. | # Introduction Source: https://docs.noxus.ai/platform/agents/introduction Conversational AI that uses tools, searches knowledge, and executes flows autonomously Agents are conversational AI assistants that combine language understanding with the ability to use tools, search knowledge bases, and execute flows. They engage in multi-turn conversations, reason about tasks, and take autonomous actions to accomplish goals. ## Available Components * [Prompts & Instructions](/platform/agents/prompts-instructions): Write effective agent instructions for consistent, quality results. * [Tools](/platform/agents/tools): Configure flows, knowledge bases, MCP, and other tools agents can use. * [Deployments](/platform/agents/deployments): Deploy agents via platform, API, SDK, or embedded chat widget. Each component provides specialized capabilities to build powerful conversational AI. The following sections explore how to configure and deploy agents effectively. # Prompts & Instructions Source: https://docs.noxus.ai/platform/agents/prompts-instructions Writing effective agent instructions Agent instructions define the agent's role, capabilities, and behavior. Well-written instructions lead to better, more consistent results. Agent Prompts Interface ## Best Practices **Be Specific** - Clear instructions lead to better results **Provide Context** - Give agents domain knowledge in instructions **Set Boundaries** - Explicitly state what agents should NOT do **Define Success** - Explain what a successful outcome looks like **Test Iteratively** - Start simple, add complexity gradually **Include Examples** - Show examples of good responses in instructions **Monitor Performance** - Review conversation logs regularly **Set Escalation Rules** - Define when to ask for human help **Add Guardrails** - Implement safety checks and limits **Content Filtering** - Prevent inappropriate outputs **Usage Limits** - Set token and cost budgets ## Common Patterns ``` You are a lead qualification agent. Your task is to gather information about potential customers by asking questions. Required information: - Company name - Industry - Company size - Primary pain points - Budget range - Timeline for decision Ask questions one at a time. After gathering all information, provide a lead score (1-100) with justification. ``` ``` You are a market research analyst. Task: Research the specified topic and create a comprehensive report. Process: 1. Use web research to gather current information 2. Search knowledge base for internal data 3. Synthesize findings 4. Create structured report with citations ``` ``` You are a business intelligence assistant. Help users query the sales database by: 1. Understanding their question in natural language 2. Generating appropriate SQL queries 3. Explaining the results clearly 4. Creating visualizations when helpful ``` ## Instruction Structure Good instructions are clear, specific, and actionable: ``` # Role You are a [role description]. # Capabilities You can: - [Capability 1] - [Capability 2] - [Capability 3] # Tasks Your task is to [primary goal]. [Specific instructions on how to accomplish the goal] # Guidelines - [Guideline 1] - [Guideline 2] - [Guideline 3] # Output Format [How to structure responses] ``` ## Examples ### Customer Support Agent ``` # Role You are a helpful customer support agent for Acme Software. # Capabilities You can: - Search the knowledge base for product documentation - Look up customer account information - Create support tickets - Escalate to human agents when needed # Tasks Help customers resolve their issues by: 1. Understanding their problem thoroughly 2. Searching the knowledge base for solutions 3. Providing clear, step-by-step guidance 4. Creating tickets for unresolved issues # Guidelines - Always be polite and professional - Don't make promises about features or timelines - If unsure, escalate to a human agent - Verify customer identity before accessing account info # Output Format Provide responses in a friendly, conversational tone. For technical solutions, use numbered steps. ``` Learn how to configure tools for your agents # Agent Sandbox Source: https://docs.noxus.ai/platform/agents/sandbox A secure, isolated environment where agents run code and build artifacts The **Sandbox** gives an agent a secure, isolated environment — a [gVisor](https://gvisor.dev) micro-VM — where it can run shell commands, execute code, install packages, and read and write files. Unlike one-shot [Code Execution](/platform/agents/tools), the sandbox **persists across the conversation**, so the agent can work step by step: fetch data, transform it, and produce a finished artifact. ## Why it's safe Each sandbox is a gVisor micro-VM with its own filesystem — code runs isolated from the platform and from other workspaces. Private/internal network ranges are blocked; the sandbox can't reach internal services. Sandboxes are torn down when idle, so nothing lingers after the task is done. Running code requires the `sandboxes:run` permission — it's never implied by general access and must be granted explicitly. ## What agents use it for * Multi-step data processing that needs a real filesystem * Building a report, spreadsheet, or document over several turns * Running scripts or tooling and returning the output as a file ## Approvals The sandbox executes real code, so you can gate it: require your **approval before the agent runs commands or writes files**. This keeps a human in the loop for higher-stakes automation while still letting the agent propose the work. Enable the sandbox per agent under [Agent Tools](/platform/agents/tools). ## Programmatic sandboxes The same sandbox capability is available directly — create a sandbox, run commands, and read/write files from code — without an agent: `client.sandboxes.create()`, `sandbox.commands.run(...)`, `sandbox.files.write/read(...)`. The REST endpoints under `/v1/sandboxes`. # Tools Source: https://docs.noxus.ai/platform/agents/tools Configure agent capabilities and tool access Agents become powerful when equipped with tools. Tools enable agents to take actions, access information, and execute complex operations. Agent Tools Interface ## Available Tools **Execute flows as tools within agent conversations** **Use Cases:** * Execute business logic * Integrate with external systems * Perform complex calculations * Trigger automated workflows **Configuration:** * Select which flows the agent can access * Map flow inputs to conversation context **When to Enable:** Enable flow tools when the agent needs to: * Perform specific, well-defined operations * Execute complex multi-node workflows * Interact with external systems * Process files or documents Create focused flows for specific tasks, then give agents access to them. **Example:** ``` Agent: "I'll look up that customer information for you." → Executes "Get Customer Info" flow → Receives customer data Agent: "Here's what I found about that customer..." ``` **Search knowledge bases for information** **Use Cases:** * Answer questions from documents * Access product documentation * Find historical information * Provide cited responses **Configuration:** * Select which knowledge bases to search * Set retrieval limits * Define search conditions **When to Enable:** Enable KB search when the agent needs to: * Answer questions based on your documents * Retrieve domain-specific information * Provide accurate, grounded responses * Cite sources Organize knowledge bases by domain (Product Docs, Company Policies, Support Articles). **Example:** ``` User: "What's our refund policy?" Agent: Searches knowledge base Agent: "According to our policy document, customers can request refunds within 30 days..." ``` **Search the web for current information** **Use Cases:** * Find recent news and updates * Research public information * Verify facts and data * Gather competitive intelligence **Configuration:** * Enable/disable web research * Set search result limits * Configure source filtering **When to Enable:** Enable web research when the agent needs: * Current information not in knowledge bases * Real-time data (stock prices, news, etc.) * Information from public sources * Broad research across multiple sites Web research is slower and less reliable than knowledge bases. Use for supplemental information. **Example:** ``` User: "What are the latest AI trends?" Agent: Searches web Agent: "Based on recent articles, the top AI trends include..." ``` **Escalate to humans for guidance or approval** **Use Cases:** * Get approval for critical actions * Ask for clarification * Escalate complex issues * Collect additional information **Configuration:** * Define escalation criteria * Set timeout policies * Configure notification channels **When to Enable:** Enable HITL when the agent should: * Get approval for critical actions * Ask for clarification on ambiguous requests * Escalate complex issues * Collect additional information Use HITL as a safety net for high-stakes decisions. **Example:** ``` Agent: "This request requires manager approval." → Sends notification to human → Waits for response Agent: "Your manager has approved the request. Proceeding..." ``` **Run code to compute, transform, and analyze data** The agent writes and executes code to do things language models are bad at on their own — precise math, parsing/reshaping data, generating charts or files. **Use Cases:** * Calculations and data transformations * Parsing CSV/JSON and producing tables or files * Ad-hoc analysis on data the agent has gathered **When to Enable:** Enable code execution when the agent needs deterministic computation or has to produce a file (spreadsheet, chart) from data in the conversation. Pair with **Data Tables** or **Knowledge Bases** so the agent can pull data in, then compute over it. **A persistent, isolated shell + code environment** Gives the agent a secure, network-jailed sandbox (a gVisor micro-VM) it can use across the conversation: run shell commands, install packages, read and write files. Unlike one-shot Code Execution, the sandbox **persists** so the agent can work iteratively. **Use Cases:** * Multi-step data processing and scripting * Building an artifact (report, spreadsheet, document) step by step * Running tooling that needs a real filesystem **When to Enable:** Enable the sandbox for longer, stateful tasks where the agent builds something up over several turns. Approval gates can require your sign-off before it runs or writes. The sandbox executes real code. It is isolated and network-jailed, but only grant it to agents you trust with the task. **Generate images from text** Lets the agent create images on request and attach them to the conversation. **Use Cases:** * Illustrations, mockups, and diagrams * Marketing/creative assets * Visual explanations **When to Enable:** Enable for creative or design-oriented agents that need to produce visuals. **Query a connected database in natural language** Connect one or more databases; the agent translates questions into **read-only** SQL and returns the results. Schema is discovered so the agent uses real table and column names. **Use Cases:** * "How many orders shipped last week?" over a production DB * Ad-hoc reporting without writing SQL by hand * Grounding answers in live operational data **Configuration:** * Select which database connection(s) the agent may query * Access is read-only by design Scope the connection to the data the agent should see; queries run with that connection's privileges. **Read and query workspace data tables** Give the agent access to your [data tables](/platform/tables/overview) — the workspace's structured, spreadsheet-like data — so it can look up and aggregate rows with read-only SQL. **Use Cases:** * Look up a record by key * Aggregate/filter rows to answer a question * Combine table data with other tools (e.g. compute over it with Code Execution) **Configuration:** * Select which tables the agent can access **Let the agent use tools from external MCP servers** Connect [Model Context Protocol](https://modelcontextprotocol.io) servers — GitHub, Linear, Notion, Slack, Stripe, and many more — and the agent can call their tools directly (create an issue, read a page, look up a customer). **Use Cases:** * Take actions in third-party systems * Pull context from tools your team already uses * Extend the agent without building a custom integration **Configuration:** * Bind one or more MCP server credentials to the agent * The agent sees each server's tools automatically This is the reverse of the [Noxus MCP server](/sdk/mcp/overview): here your Noxus agent is the *client* consuming an external MCP server. Learn how to deploy and manage agents # Analytics API Source: https://docs.noxus.ai/platform/analytics-api/overview Query workspace metrics programmatically via the REST API The Analytics API lets you retrieve workspace metrics programmatically using the same data that powers the in-app analytics dashboards. All queries are scoped to the workspace identified by your API key. For interactive endpoint documentation with request/response examples, see the [API reference](/api-reference/introduction). Authentication follows the same API key mechanism used across all v1 endpoints — refer to the [API reference](/api-reference/introduction) for details. *** ## Endpoint ``` GET /v1/analytics/{metric} ``` ### Path parameter | Parameter | Type | Description | | --------- | ------ | --------------------------------------------------------------------------------------------------- | | `metric` | string | The analytics metric to retrieve. See [Available Metrics](#available-metrics) for all valid values. | ### Query parameters | Parameter | Type | Required | Description | | ------------ | ----------------- | -------- | ----------------------------------------------------------------------------------------------------------------- | | `time_start` | ISO 8601 datetime | Yes | Start of the time range. Must include a timezone offset (e.g. `2026-01-01T00:00:00Z`). Must be before `time_end`. | | `time_end` | ISO 8601 datetime | Yes | End of the time range. Must include a timezone offset. | | `page` | integer | No | Page number for paginated metrics (1-based). | | `page_size` | integer | No | Number of items per page for paginated metrics. Default: 50. Max: 200. | ### Time range resolution The API automatically adjusts aggregation granularity based on the requested range: * **More than 3 days** → daily buckets * **3 days or less** → hourly buckets ### Rate limits 5 requests per second per workspace. *** ## Response format All metrics return an `AnalyticsResponse` object with two fields: | Field | Type | Description | | ------- | ------ | ----------------------------------------------------------------------- | | `type` | string | One of `simple`, `table`, `barchart`, `horizontal-barchart`, or `empty` | | `value` | object | The metric data. Shape depends on `type` (see below). | ### `simple` — a single scalar value ```json theme={null} { "type": "simple", "value": { "value": 1234, "prefix": null, "suffix": " runs" } } ``` | Field | Description | | -------- | ---------------------------------------------------------- | | `value` | The numeric or string value | | `prefix` | Optional string displayed before the value (e.g. `"$"`) | | `suffix` | Optional string displayed after the value (e.g. `" runs"`) | ### `barchart` — time-series data ```json theme={null} { "type": "barchart", "value": { "values": [ { "name": "2026-01-15", "values": { "runs": 42 }, "value_mapping": null }, { "name": "2026-01-16", "values": { "runs": 37 }, "value_mapping": null } ] } } ``` Each item in `values` represents one time bucket. The keys inside `values` are metric-specific series names. ### `table` — tabular summary ```json theme={null} { "type": "table", "value": { "headings": ["Model", "Tokens", "Cost"], "rows": [ { "Model": "gpt-4o", "Tokens": 50000, "Cost": 0.25 } ], "prefixes": { "Cost": "$" }, "suffixes": null } } ``` ### `horizontal-barchart` — ranked distribution Same shape as `table`, displayed as a horizontal bar chart in the UI. ### `empty` Returned when there is no data for the requested time range. ### Error responses **400 — invalid time range** ```json theme={null} { "detail": "time_start must be before time_end" } ``` **422 — validation error** (e.g. `page_size` out of bounds) ```json theme={null} { "detail": [ { "loc": ["query", "page_size"], "msg": "ensure this value is less than or equal to 200", "type": "value_error.number.not_le" } ] } ``` **429 — rate limit exceeded** ```json theme={null} { "detail": "Rate limit exceeded" } ``` *** ## Available metrics ### Execution metrics | Metric | Response type | Description | | ------------------------ | ------------- | ------------------------------------------------------------- | | `flow_runs` | `simple` | Total flow executions in the time range | | `trigger_runs` | `simple` | Executions initiated by triggers | | `manual_runs` | `simple` | Executions initiated manually (editor or API) | | `agent_runs` | `simple` | Executions initiated by agents | | `active_users` | `simple` | Distinct users who ran flows | | `active_runs` | `simple` | Flows currently executing (live count) | | `active_triggers` | `simple` | Currently enabled triggers | | `errors` | `simple` | Number of failed runs | | `average_run_time` | `simple` | Mean flow execution duration | | `flow_runs_distribution` | `barchart` | Run count over time, split by source (manual, trigger, agent) | | `flow_per_user` | `table` | Run count per user | | `errors_over_time` | `barchart` | Failed runs over time | | `run_time_over_time` | `barchart` | Execution duration over time (P25, P50, P90 percentiles) | ### Cost & token metrics | Metric | Response type | Description | | ------------------------- | ------------- | --------------------------------------------------------- | | `ai_models_total_cost` | `simple` | Estimated total LLM cost across all models | | `ai_models_total_tokens` | `simple` | Total tokens consumed across all models | | `average_cost_per_run` | `simple` | Mean estimated cost per flow execution | | `tokens_cost` | `table` | Tokens and estimated cost grouped by LLM model | | `tokens_cost_by_workflow` | `table` | Tokens and estimated cost per workflow | | `tokens_cost_by_user` | `table` | Tokens and estimated cost per user | | `tokens_cost_by_node` | `table` | Tokens and estimated cost per node | | `model_tokens_total_cost` | `simple` | Estimated total cost (non-cached tokens only) | | `ai_operations_per_tool` | `table` | AI operation count grouped by tool type (flow, agent, KB) | | `model_tokens_per_tool` | `table` | Token usage grouped by tool type | | `ai_operations_per_user` | `table` | AI operation count per user | | `model_tokens_per_user` | `table` | Token usage per user | Costs are estimates based on publicly available model pricing. Cached LLM responses are excluded from cost calculations. ### Conversation metrics | Metric | Response type | Description | | ---------------------------------- | ------------- | ---------------------------------------------------- | | `conversations_started` | `simple` | Total conversations initiated | | `messages_sent` | `simple` | Total messages sent (user + agent) | | `messages_per_conversation` | `simple` | Average messages per conversation | | `conversation_users` | `simple` | Distinct users who started conversations | | `conversations_started_over_time` | `barchart` | Conversations started over time | | `messages_sent_over_time` | `barchart` | Messages sent over time | | `conversations_started_by_user` | `table` | Conversations started per user | | `messages_sent_by_user` | `table` | Messages sent per user | | `conversation_tokens_cost` | `table` | Token usage and cost per LLM model for conversations | | `conversation_tokens_cost_by_user` | `table` | Conversation token usage and cost per user | | `conversation_chat_costs_total` | `simple` | Estimated total cost of all conversation LLM calls | ### API usage metrics | Metric | Response type | Description | | ------------------------ | ------------------- | ----------------------------------------------------------------------------------- | | `api_calls_per_date` | `barchart` | API calls made per day/hour | | `api_calls_per_endpoint` | `table` | API call count grouped by endpoint | | `api_calls_log` | `table` (paginated) | Detailed log of recent API calls with endpoint, method, response code, and duration | ### Knowledge base metrics | Metric | Response type | Description | | --------------------------- | ------------- | ------------------------------------------------------------ | | `kb_total_documents` | `simple` | Total documents across all knowledge bases | | `kb_processed_successfully` | `simple` | Documents successfully ingested and indexed | | `kb_failed_documents` | `simple` | Documents that failed ingestion | | `kb_contributors` | `simple` | Distinct users who uploaded documents | | `kb_documents_over_time` | `barchart` | Document ingestion count over time | | `kb_documents_per_kb` | `table` | Document count per knowledge base | | `kb_documents_by_user` | `table` | Document count per user | | `kb_document_types` | `table` | Document count grouped by file type (PDF, Word, Excel, etc.) | | `kb_user_activity` | `table` | Per-user breakdown of upload activity | | `kb_overview` | `table` | Summary table across all knowledge bases | *** ## Examples ### Total flow runs for January 2026 ```bash cURL theme={null} curl -X GET \ "https://api.noxus.ai/v1/analytics/flow_runs?time_start=2026-01-01T00:00:00Z&time_end=2026-01-31T23:59:59Z" \ -H "X-API-Key: your_api_key_here" ``` ```python Python theme={null} import requests response = requests.get( "https://api.noxus.ai/v1/analytics/flow_runs", headers={"X-API-Key": "your_api_key_here"}, params={ "time_start": "2026-01-01T00:00:00Z", "time_end": "2026-01-31T23:59:59Z", }, ) data = response.json() print(f"Total runs: {data['value']['value']}") ``` **Response:** ```json theme={null} { "type": "simple", "value": { "value": 1842, "prefix": null, "suffix": " runs" } } ``` *** ### LLM token usage by model (last 7 days) ```bash cURL theme={null} curl -X GET \ "https://api.noxus.ai/v1/analytics/tokens_cost?time_start=2026-02-20T00:00:00Z&time_end=2026-02-27T23:59:59Z" \ -H "X-API-Key: your_api_key_here" ``` ```python Python theme={null} import requests response = requests.get( "https://api.noxus.ai/v1/analytics/tokens_cost", headers={"X-API-Key": "your_api_key_here"}, params={ "time_start": "2026-02-20T00:00:00Z", "time_end": "2026-02-27T23:59:59Z", }, ) data = response.json() for row in data["value"]["rows"]: print(row) ``` **Response:** ```json theme={null} { "type": "table", "value": { "headings": ["Model", "Tokens", "Cost"], "rows": [ { "Model": "gpt-4o", "Tokens": 250000, "Cost": 1.25 }, { "Model": "claude-3-5-sonnet", "Tokens": 180000, "Cost": 0.54 } ], "prefixes": { "Cost": "$" }, "suffixes": null } } ``` *** ### Paginated API call log ```bash cURL theme={null} curl -X GET \ "https://api.noxus.ai/v1/analytics/api_calls_log?time_start=2026-02-01T00:00:00Z&time_end=2026-02-27T23:59:59Z&page=1&page_size=20" \ -H "X-API-Key: your_api_key_here" ``` **Response:** ```json theme={null} { "type": "table", "value": { "headings": ["Endpoint", "Method", "Status", "Duration"], "items": [ { "Endpoint": "/v1/workflows/{id}/runs", "Method": "POST", "Status": 200, "Duration": "142ms" } ], "total": 1560, "pages": 78, "size": 20, "page": 1 } } ``` # Agent Insights Source: https://docs.noxus.ai/platform/analytics/insights Automatic analytics over an agent's conversations — CSAT, topics, sentiment, and drivers Where the [Analytics overview](/platform/analytics/overview) covers volume and cost, **Agent Insights** analyzes the *content* of an agent's conversations to tell you what users ask about, how they feel, and what drives satisfaction — computed automatically, no tagging required. ## What you get An estimated customer-satisfaction score over a time window, trended over time. The themes users bring up, ranked by volume, with drill-down into sub-topics. Sentiment across conversations and how it moves over time. What correlates with high vs. low satisfaction — the levers to act on. How conversations progress and where they drop off. Automatically surfaced notable conversations and patterns worth a look. ## Filtering & drill-down Every metric can be scoped by: * **Time window** — the last *N* days. * **Deployment** — narrow to a single channel (e.g. the Slack deployment vs. the web widget). * **Message length** — short / medium / long, to separate quick pings from substantive chats. From any topic, driver, or sub-topic you can **drill down into the underlying conversations** to read exactly what users said. Insights are computed asynchronously as conversations accumulate. A brand-new agent shows a "warming up" state until there's enough data — check the bootstrap status if a dashboard looks empty. ## Access it programmatically The same dashboards are available over the API and SDK, so you can pull insights into your own reporting: `client.insights.top_topics(...)`, `.csat_score(...)`, `.sentiment_over_time(...)`, and more. The REST endpoints under `/v1/agents/{id}/insights/*`. # Overview Source: https://docs.noxus.ai/platform/analytics/overview How metrics are collected across flows, agents, and knowledge bases, and where to access them Noxus automatically collects metrics for every building block on the platform — flows, agents, and knowledge bases. No configuration is required: every execution, conversation, and document ingestion is tracked and made available through both the in-app dashboards and the REST API. ## What's measured Every flow execution is tracked: run count, duration, error rate, AI model usage, token consumption, and cost — broken down by user, trigger source, and node. Every conversation and message is counted. Analytics include active users, messages per conversation, and full LLM cost breakdowns by model and user. Every document upload and ingestion job is tracked: total documents, processing status (succeeded/failed), file types, and contributor activity. The metrics also include extensive AI model analytics — covering LLM and embedding model usage, token consumption, estimated costs per model, and how that usage breaks down across flows, agents, nodes, and users. *** ## Where to access analytics ### Resource-level analytics Each individual resource has its own **Analytics** tab. Open any flow, agent, or knowledge base and click the **Analytics** tab in the top navigation to view metrics scoped to that resource. Navigate to a flow and click the **Analytics** tab. Available metrics include: * Total runs (manual, triggered, agent-initiated) * Active and currently running flows * Error rate and error distribution over time * Average run time with percentile breakdown * Token and cost usage by model, node, and user * Run distribution by source type over time Navigate to an agent (co-worker) and click the **Analytics** tab. Available metrics include: * Conversations started and messages sent * Average messages per conversation * Active users * Conversation volume over time * LLM token and cost breakdown by model and user Navigate to a knowledge base and click the **Analytics** tab. Available metrics include: * Total documents and processing status * Document ingestion over time * Documents by knowledge base, user, and file type * Contributor activity ### Workspace-level analytics To see aggregated metrics across all resources in a workspace, navigate to **Settings → Analytics**. This view combines flow runs, agent conversations, knowledge base activity, API usage, and AI model costs into a single cross-resource dashboard. Workspace analytics are scoped to the workspace you are currently in. Switch workspaces from the workspace selector to view analytics for a different workspace. ### Billing analytics Usage against your plan quotas is tracked separately. Navigate to **Settings → Billing → Analytics** to view: * AI operations consumed vs. your plan limit * Operations over time * Usage per workspace (for tenant admins) * Model token consumption and associated costs Billing analytics reflect the constraints defined on your subscription. Contact your administrator if you need to review or adjust quota limits. *** ## Accessing analytics programmatically All of the metrics available in the in-app dashboards can also be retrieved via the REST API, using the same API key you use for workflows and conversations. See [Analytics API](/platform/analytics-api/overview) for the full endpoint reference and available metrics. # Agents Source: https://docs.noxus.ai/platform/concepts/agents Conversational AI that uses tools and executes tasks ## What is an Agent? An agent in Noxus is a conversational AI assistant built by connecting capabilities into an intelligent interface. Agents enable: * **Natural Language Processing**: Process requests naturally, maintain context, and adapt to user needs * **Tool Integration**: Access web research, human-in-the-loop, knowledge bases, flow execution, and file handling * **Context Awareness**: Remember conversation history and maintain context across interactions * **Smart Decision Making**: Make intelligent choices based on context and available tools Agents combine natural language understanding with the ability to take actions—creating truly autonomous AI assistants. ## What are Agents Used For? Agents enable intelligent, conversational automation that can reason and adapt. * Answer customer questions * Troubleshoot issues * Create support tickets * Escalate to humans when needed * Help employees find information * Execute business processes * Generate reports and summaries * Automate routine tasks * Gather information from multiple sources * Synthesize findings * Generate comprehensive reports * Provide insights and recommendations * Multi-step task execution * Dynamic decision making * Tool orchestration * Context-aware processing ## Key Features Interact using conversational language Execute flows, search knowledge bases, call APIs Remember conversation history and maintain context Plan multi-step approaches and adapt based on results Search and retrieve information from knowledge bases Escalate to humans when needed ## When to Use Agents * Conversational interface needed * Dynamic, context-aware decisions required * Natural language interaction preferred * Multi-turn conversations * Tool use and reasoning needed * Autonomous task execution * Predefined, structured process * Visual workflow design preferred * No conversation needed * Deterministic execution required * Complex data transformations ## Agent Components Agents are configured with several key elements: | Component | Description | Examples | | :--------------- | :------------------------------- | :------------------------------------------------ | | **Instructions** | Natural language role definition | "You are a customer support agent for..." | | **Tools** | Capabilities the agent can use | Flows, knowledge bases, APIs, web research | | **Model** | Underlying AI engine | GPT-4, Claude Opus, GPT-4o | | **Memory** | Context management | Conversation history, session state | | **Guardrails** | Safety and limits | Token limits, content filtering, escalation rules | Complete guide to agents and their capabilities *** ## Next Steps Learn how to write effective agent instructions Configure tools and capabilities for agents # Flows Source: https://docs.noxus.ai/platform/concepts/flows Visual automation for AI-powered workflows ## What is a Flow? A flow is a visual automation built by connecting nodes into a directed graph. Each node performs a specific operation, and connections define how data moves through the process. **Key Benefits:** * Visual design for complex automation logic * Clear data flow and dependencies * Built-in error handling and recovery * Real-time execution monitoring * Comprehensive analytics and observability Flows are blueprints for automation—showing how operations connect and work together to accomplish tasks. ## What are Flows Used For? Flows enable you to build sophisticated AI automation without writing code. * Extract, transform, and load data * Process documents and files * Integrate multiple data sources * Generate reports and analytics * Text generation and analysis * Image processing and vision tasks * Multi-step AI reasoning * Structured data extraction * Workflow orchestration * Approval processes * Notification systems * Scheduled tasks * Connect multiple services and APIs * Sync data between systems * Webhook processing * Event-driven automation ## Key Features Drag-and-drop interface for building complex logic without code Automatic validation ensures data compatibility between nodes Test flows with sample data and inspect outputs at each step Built-in error handling and recovery mechanisms Flows scale automatically based on demand Complete execution logs, metrics, and analytics ## When to Use Flows * You need visual, no-code automation * Multiple steps or operations required * Data transformation is needed * Integration with external services * Scheduled or triggered execution * Team collaboration on automation * Conversational interface needed * Dynamic, context-aware decisions * Natural language interaction * Multi-turn conversations * Tool use and reasoning required ## Flow Components Flows are built from several key components: | Component | Description | Purpose | | :-------------- | :-------------------------- | :------------------------------------------------ | | **Nodes** | Individual operations | AI processing, data transformation, logic control | | **Connections** | Data flow between nodes | Type validation and data routing | | **Inputs** | Entry points for data | Pass data into the flow | | **Outputs** | Results returned | Extract results from the flow | | **Triggers** | Events that start execution | Schedule, webhook, API calls | | **Subflows** | Reusable flows | Modular components within flows | Complete guide to flows and their capabilities *** ## Next Steps Explore inputs, outputs, nodes, and triggers See flows in action with real examples # Knowledge Bases Source: https://docs.noxus.ai/platform/concepts/knowledge-bases Semantic search powered by your documents and data ## What is a Knowledge Base? Knowledge Bases in Noxus are intelligent data repositories that enhance AI capabilities with domain-specific information. They process and store information in a way that makes it readily accessible for AI operations, maintaining context and relationships between different pieces of information. Unlike simple file storage, Knowledge Bases understand the meaning of content and can retrieve relevant information based on semantic similarity, not just keyword matching. ## What are Knowledge Bases Used For? Knowledge Bases power AI with your organization's knowledge. * Answer questions from documents * Provide accurate, cited responses * Search across large document collections * Multi-document synthesis * Semantic search that understands intent * Find relevant information quickly * Cross-reference multiple sources * Retrieve specific passages * Give agents access to company information * Enable context-aware responses * Provide up-to-date data * Support decision-making with facts * Extract insights from documents * Summarize large collections * Find patterns and connections * Generate reports from data ## Key Features Understands meaning and intent, not just keywords PDFs, Word docs, spreadsheets, images, and more Extracts text, chunks documents, generates embeddings Responses include source references Add or update documents anytime Workspace-level permissions and security ## When to Use Knowledge Bases * AI needs access to your documents * Question answering from data * Semantic search required * Citation and sources important * Large document collections * Frequently updated information * Simple keyword search sufficient * Structured database queries needed * Real-time data from APIs * No document processing required ## Knowledge Base Components | Component | Description | Purpose | | :---------------- | :------------------------ | :--------------------------- | | **Documents** | Files uploaded or synced | Source content for search | | **Embeddings** | Vector representations | Enable semantic search | | **Chunks** | Document segments | Optimized retrieval units | | **Metadata** | Tags and attributes | Filtering and organization | | **Search Config** | Retrieval methods | Control search behavior | | **Integrations** | Cloud storage connections | Google Drive, OneDrive, etc. | Complete guide to knowledge bases and their capabilities *** ## Next Steps Learn what file types and integrations are supported Configure search methods and retrieval settings # Monitoring & Analytics Source: https://docs.noxus.ai/platform/concepts/monitoring Track usage, performance, and costs across flows, agents, and knowledge bases Every flow, agent, and knowledge base includes a built-in Analytics page that provides comprehensive visibility into usage, performance, and costs. These analytics help you optimize AI operations, track resource consumption, and monitor system health. ## What's Tracked All AI operations are tracked and measured automatically. An **AI operation** is counted for: * Each node execution in a flow * Each message sent to or from an agent * Each file ingested into a knowledge base Analytics data is available in real-time and can be filtered by time period, user, and specific resources. ## Flow Analytics Flow analytics provide detailed insights into execution patterns, performance, and resource usage. **Flow Runs** * Total runs (manual, triggered, and from agents) * Active runs currently executing * Run distribution over time * Runs per user **Performance** * Average run time * Run time over time with percentile analysis (P25, P50, P90) * Active triggers * Error rate and error distribution over time **Token Tracking** * Tokens & cost by AI model * Tokens & cost by node * Tokens & cost per user * Average cost per run **Cost Analysis** All costs are estimated based on current model pricing from providers (OpenAI, Anthropic, Google, etc.). Actual costs may vary based on your agreements with providers. * Flow runs per user * Token usage per user * Cost attribution per user * Active users in the selected time period ## Agent Analytics Agent analytics track conversational interactions, message volumes, and AI model usage. **Activity** * Conversations started * Messages sent (user and agent messages) * Messages per conversation average * Active users **Trends** * Conversations started over time * Messages sent over time * Conversations started per user * Messages sent per user **Token Tracking** * Tokens & cost by AI model * Tokens & cost per user * Total chat estimated cost **Cost Breakdown** Agent costs include all AI model interactions during conversations, including tool calls, knowledge base queries, and response generation. ## Knowledge Base Analytics Knowledge base analytics monitor document ingestion, processing status, and user contributions. **Ingestion Status** * Total documents uploaded * Documents processed successfully * Failed documents * Documents over time **Distribution** * Documents per knowledge base * Documents by user * Document types (PDF, Word, Excel, etc.) **Contributors** * Documents uploaded by user * Active contributors * User activity table with detailed breakdown ## AI Operations AI operations represent the fundamental unit of resource consumption across the platform. **What counts as an AI operation:** * **Flow Nodes**: Each node execution (LLM calls, embeddings, AI processing) * **Agent Messages**: Each user message and agent response * **KB Ingestion**: Each file ingested and processed AI operations are used for billing and quota management. Each workspace has AI operation limits based on its subscription tier. ## Workspace-Level Analytics In addition to resource-specific analytics, workspace-level analytics provide a unified view across all flows, agents, and knowledge bases. **Key Metrics:** * Total AI operations used * AI models estimated cost * AI models total tokens * AI operations over time * Model tokens over time * AI operations per tool (flows, agents, KBs) * Model tokens per tool ## Accessing Analytics Analytics are available in two locations: 1. **Resource-specific**: Click the Analytics tab on any flow, agent, or knowledge base 2. **Workspace-level**: Navigate to Workspace control > Analytics for aggregated metrics Use time period filters to compare performance across different timeframes and identify trends. # Permissions & Roles Source: https://docs.noxus.ai/platform/concepts/permissions In-depth guide to Noxus permissions, default roles, and custom role management Noxus provides a Role-Based Access Control (RBAC) system that manages user access at both the organization and workspace levels. Each user has one organization-level role (controlling tenant-wide operations) and optionally a role per workspace (controlling what they can do inside that workspace). *** ## Role Scope Roles in Noxus have two scopes: * **Global roles** — apply across all workspaces. A user assigned a global role has the same workspace permissions in every workspace they belong to. * **Workspace-scoped roles** — apply only to a specific workspace. Useful for giving a user different access levels in different workspaces. *** ## Default Roles The following built-in role configurations cover the most common use cases. These can be used as starting points when creating roles for your organization. ### Organization Roles | Role | Description | Key permissions | | :------------ | :----------------------------------------- | :------------------------------------------------------------------------------ | | **Org Admin** | Full control over the entire organization. | All org permissions: `users.*`, `workspace.*`, `org.*`, `settings_read`. | | **Org Base** | Standard organization member. | `users_read`, `workspace_read`, `workspace_write`, `org_read`, `settings_read`. | ### Workspace Roles | Role | Description | Key permissions | | :--------- | :----------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------ | | **Admin** | Full control over a specific workspace. | All workspace permissions including `workspace_admin`. | | **Editor** | Can create and manage AI resources. | `flows_edit`, `flows_delete`, `flows_run`, `agents_edit`, `agents_delete`, `agents_run`, `kbs_edit`, `kbs_delete`, `kbs_query`. | | **Reader** | Run and query resources, no modifications. | `flows_run`, `agents_run`, `kbs_query`. | *** ## Creating Custom Roles If the default roles don't meet your needs, create custom roles with a specific set of permissions. **Organization-level roles:** 1. Navigate to **Settings** → **Roles** → **Organization** tab. 2. Click **Create role**. 3. Provide a name and optional description. 4. Select the desired organization-level permissions. 5. Click **Save**. **Workspace roles:** 1. Navigate to **Settings** → **Roles** → **Workspace** tab. 2. Use the workspace picker to choose a specific workspace or **All workspaces** (creates a global role). 3. Click **Create role**. 4. Provide a name and optional description. 5. Select the desired workspace-level permissions. 6. Click **Save**. Roles created with **All workspaces** selected are global roles and apply to every workspace. Roles created for a specific workspace only apply to members of that workspace. *** ## Assigning Roles to Users Roles can be assigned when inviting a user or updated later in the user management section. ### At the Organization Level 1. Go to **Settings** → **Users**. 2. Select a user and click **Edit role**. 3. Choose the appropriate organization-level role. ### At the Workspace Level 1. Navigate to a specific workspace's **Settings** → **Users**. 2. Click **Add users** or edit an existing member's role. 3. Select the workspace-specific role. *** ## API Key Permissions When creating a workspace API key you can optionally restrict it to a subset of workspace permissions. A key with no restrictions has full access to all workspace permissions. A restricted key can only be granted permissions the creating user already has. For a full reference of permission keys, see [Permissions Overview](/core/concepts/permissions). # Deployment Source: https://docs.noxus.ai/platform/flows/deployments Deploy flows as interactive forms accessible to your workspace users Deploy flows as interactive applications that your workspace users can access directly from the Noxus platform. A deployment gives users a dedicated URL where they can provide inputs, run the flow, and see results — all without touching the editor. ## Form Deployment Form is the quickest way to put a flow in front of a user. No setup is needed beyond publishing the flow and choosing who is allowed to use it. Form interface ### Deployment | Field | Description | | :---------- | :----------------------------------------------------------------------------------------------------------------------------------------- | | **Status** | Toggle the deployment **Online** or **Offline**. A published version is required before going online. | | **Version** | The published flow version this deployment uses. | | **App URL** | Authenticated URL where users can access the form. Shown once the deployment is online — requires users to be logged in to your workspace. | A published **version** is required before you can set a deployment to Online. Switch the deployment to **Offline** before editing its configuration. ### Access Control who in your workspace can open the form. | Field | Description | | :-------------------------------------- | :------------------------------------------------------------------------------------------ | | **Custom Instructions** | Per-deployment instructions appended to the flow's system prompt. | | **Allow all app users to use this app** | When enabled, every **App user** in the workspace can open the form. | | **Allowed users** | When the toggle above is off, pick a specific list of users who can access this deployment. | ### Display | Field | Description | | :----------------------- | :--------------------------------------------------------------------------- | | **Show output progress** | When enabled, users see which nodes are being processed while the flow runs. | ## Next Steps * Read the [API Reference](/api-reference/v1/conversations/public-create-conversation) for programmatic access * Learn about [Agents](/platform/agents/introduction) for conversational deployments * Explore [Knowledge Bases](/platform/knowledge-bases/introduction) to power your flows and agents with document retrieval # Inputs & Outputs Source: https://docs.noxus.ai/platform/flows/inputs-outputs Essential nodes for data entry and result delivery in flows ## What are Inputs & Outputs? Input and Output nodes are the fundamental entry and exit points for data in your flows. They define how information enters your flow, gets processed through various nodes, and returns results to users or systems. ### Node Types | Node Type | Function | Supported Types | | :-------------- | :--------------------------------------------- | :----------------------- | | **Input Node** | Receives data into flows from external sources | Text, Image, Audio, File | | **Output Node** | Returns processed data to users or systems | Text, Image, Audio, File | Input Node Configuration ## Core Capabilities **Type Configuration** - Input and Output nodes require explicit type selection from supported data types (Text, Image, Audio, File). You must configure the expected data type during node setup, and the node will enforce this type throughout flow execution. Input nodes have a "Type" configuration field where you select from available data types. This determines both the input connector type and the output connector type. **Fixed Value Support** - Input nodes can be configured with fixed values that remain constant between flow executions. This is useful for configuration parameters, default values, and testing scenarios. Input nodes with fixed values cannot accept external input - they always return the configured constant value. **External Data Sources** - Input nodes receive data when flows are called via API, webhooks, triggers, or other external sources. This requires the flow to be executed through API calls rather than running directly in the flow editor, providing standardized data ingestion points for programmatic flow execution. **Output Formatting** - Output nodes pass through received data in the configured type format. For String outputs with list inputs, the system automatically formats list items with bullet points for readability. **Specialized Input/Output Nodes** - Integration nodes like Read Email, Read Excel, and Send Email function as specialized entry and exit points for flows. Unlike basic Input and Output nodes, these require integration-specific configuration and authentication. ## Next Steps * Learn about [Nodes](/platform/flows/nodes) * Explore [Integrations](/platform/flows/integrations) * Check out the [Platform](/platform/introduction) # Introduction Source: https://docs.noxus.ai/platform/flows/introduction Exploring the essential building blocks for creating flows Noxus flows are built using a variety of specialized nodes, each designed for specific tasks. These nodes are organized into core modules that make it easy to find and use the right tool for each job. ## Available Modules * [Inputs & Outputs](/platform/flows/inputs-outputs): Manage data flow in and out of flows. * [Nodes](/platform/flows/nodes): Explore AI Processing, Data & Files, and Logic & Control nodes in one place. * [Integrations](/platform/flows/integrations): Connect with external services and systems. * [Triggers](/platform/flows/triggers): Automate flow execution with events and schedules. Each module contains nodes that are optimized for their specific purpose, making it easy to build complex flows by combining these building blocks. The following sections will explore each module in detail. # Nodes Source: https://docs.noxus.ai/platform/flows/nodes Unified reference for core node categories in Noxus ## What are Nodes? Nodes are the building blocks of flows. Each node performs a specific function, defines typed inputs/outputs, and can be composed to create reliable automations. Nodes are organized by capability so you can quickly find the right tool for the job. The examples below showcase common nodes in each category. Many more nodes are available in the platform. ## Data & Files Data & Files nodes transform, extract, and generate content from files and data. **Example Nodes:** File to Text, Text to File, Download File, Merge Files **What They Do:** * Extract text from PDFs, Word docs, and other file formats * Generate files from text or data * Download files from URLs * Combine multiple files into archives **Use Case Example:** Use OCR to extract text from scanned invoice images, read and parse invoice data (amounts, dates, vendors), then analyze for expense reporting. **Example Nodes:** File to Base64, File to Path **What They Do:** * Convert files to Base64 encoding for API transmission * Get file paths for local file operations * Transform data formats for compatibility **Use Case Example:** Convert an image file to Base64 to include it directly in API requests or email bodies. **Example Nodes:** Overlay Image on PDF, Fill File Template, Fill Excel Template, PDF Form Filler **What They Do:** * Fill document templates with dynamic data * Add images, signatures, or watermarks to PDFs * Populate Excel templates with calculated values * Complete PDF forms programmatically **Use Case Example:** Generate personalized contracts by filling a Word template with customer data, then convert to PDF with company logo overlay. **Example Nodes:** Read Webpage, Make API Request, Browser Playbook **What They Do:** * Scrape content from web pages * Call external APIs with custom parameters * Fetch data from web services * Record and replay multi-step browser automations * Integrate with third-party platforms **Use Case Example:** Fetch product pricing from competitor websites, then call your internal API to update pricing strategy. The **Browser Playbook** node has its own guide — see [Browser Playbook](/platform/flows/browser-playbook) for recording, configuration, inputs/outputs, and examples. **Example Nodes:** Run Code **What They Do:** * Execute custom Python code within flows * Implement complex calculations or logic * Use external Python libraries * Process data with custom algorithms **Use Case Example:** Run a custom Python script to analyze sales data using pandas and generate statistical insights. ## Logic & Control Logic & Control nodes route, filter, and transform data to shape execution. **Example Nodes:** Condition **What They Do:** * Create if/then branching logic * Route data based on conditions * Filter items based on criteria * Make decisions using AI or rules **Use Case Example:** Route support tickets to different teams based on priority: high-priority to urgent queue, others to standard queue. **Example Nodes:** Filter List Values, List to Single Value, Single Value to List, Count List Items **What They Do:** * Filter lists based on conditions * Convert between single values and lists * Count items in collections * Aggregate and summarize data **Use Case Example:** Filter a list of customer emails to find only those from gmail.com domains, then count how many were found. **Example Nodes:** Join Paths, Extract from Structured **What They Do:** * Merge multiple execution paths * Extract specific fields from JSON or objects * Synchronize parallel operations * Manage complex flow routing **Use Case Example:** Run two API calls in parallel, wait for both to complete with Join Paths, then extract specific fields from each response. **Example Nodes:** Combine Text, Extract Fields, Merge File and Text, Markdown to HTML **What They Do:** * Concatenate multiple text strings * Extract structured data from text using AI * Combine file content with additional text * Convert markdown to HTML format **Use Case Example:** Combine customer name, order number, and shipping address into a formatted tracking email body. ## AI Processing AI Processing nodes understand and generate content using language and vision models. **Example Nodes:** Generate Text, Generate Fields, Categorizer **What They Do:** * Generate human-like text from prompts * Extract structured data from unstructured text * Classify content into categories * Complete forms or templates intelligently **Use Case Example:** Analyze customer review text, categorize sentiment (positive/negative/neutral), and extract key product mentions. **Example Nodes:** Interpret Image, Generate Image, Transform Image, Interpret Audio **What They Do:** * Analyze and describe image content * Generate images from text descriptions * Edit or transform images * Transcribe and analyze audio **Use Case Example:** Process receipt images to extract line items, amounts, and vendor information for expense reporting. **Example Nodes:** Agent, Web Research **What They Do:** * Perform multi-step reasoning tasks * Use tools and integrations autonomously * Research topics using web search * Execute complex decision-making workflows **Use Case Example:** Research a company using web search, extract key information, check it against your CRM, and prepare a summary report. **Example Nodes:** Summarizer, Smart Route, Enrich Company **What They Do:** * Summarize long documents or text * Intelligently route data to different paths using AI * Enrich company data with public information * Perform domain-specific analysis **Use Case Example:** Read a lengthy industry report, summarize key findings, and enrich mentioned companies with revenue and employee data. ## Integrations Integration nodes connect your flows to external services and platforms. Each integration node requires authentication configuration and provides specialized functionality for its respective service. **Common Integration Examples:** * **Communication:** Send Email, Read Email, Slack, Teams * **Storage:** Google Drive, Dropbox, OneDrive * **CRM:** Salesforce, HubSpot * **Productivity:** Calendar, Tasks, Sheets **Use Case Example:** When a new lead is added to Salesforce, automatically send a welcome email via Gmail and create a task in your project management tool. Integrations have a dedicated section in the documentation. Learn more about [available integrations](/platform/flows/integrations) and how to configure them. ## Subflows Subflows are reusable flows that function as single nodes within other flows. They encapsulate complex logic into modular building blocks, enabling you to build maintainable and scalable automation. Nodes Interface **Key Benefits:** * Reuse common logic across multiple flows * Simplify complex flows by breaking them into smaller components * Maintain consistency through centralized updates * Test and debug isolated functionality independently **How They Work:** Subflows define their own inputs and outputs, just like regular flows. When used as a node in another flow, they appear with input connectors matching their defined inputs and output connectors for their results. Any changes to a subflow automatically propagate to all flows using it. **Use Case Example:** Create a "Customer Validation" subflow that checks email format, validates phone numbers, and looks up existing records. Use this subflow in multiple flows (signup, checkout, profile update) to maintain consistent validation logic. Create a subflow by building a regular flow and marking it as reusable. It will then appear in your node catalog under the Subflows category. ## Next Steps * Explore [Inputs & Outputs](/platform/flows/inputs-outputs) * Learn about [Integrations](/platform/flows/integrations) * See [Triggers](/platform/flows/triggers) # Triggers Source: https://docs.noxus.ai/platform/flows/triggers Automation catalysts that initiate flows based on events, schedules, and external signals ## What are Triggers? Triggers are the automation catalysts that initiate flow execution based on various events, schedules, and external signals. They transform reactive flows into proactive automation systems by monitoring for specific conditions and automatically starting flows when those conditions are met. Triggers bridge the gap between manual flow execution and fully automated systems, enabling flows to respond to real-world events, time-based schedules, and external system changes without human intervention. ## Available Trigger Types | Trigger Category | Example Triggers | Primary Functions | | :------------------ | :---------------------------------------- | :------------------------------------------------------------------ | | **Schedule-Based** | Schedule (Hourly, Daily, Weekly, Monthly) | Recurring automation, periodic processing, scheduled tasks | | **Communication** | Slack, Teams, Gmail | Message-driven automation, communication flow triggers | | **File Monitoring** | Google Drive, OneDrive | File change detection, document processing automation | | **Web Integration** | Webhook | Real-time response, external system integration, instant processing | Triggers Interface Triggers require a saved flow version to function. You must save a version of your flow before creating or connecting a trigger. Each trigger is associated with a specific flow version, ensuring consistent execution behavior. ## Core Capabilities **Schedule-Based Automation** - Configure flows to run automatically on specific schedules including hourly, daily, weekly, and monthly intervals. Support for custom timing, timezone handling, and flexible scheduling patterns to match business requirements. Schedule triggers handle timezone conversions, daylight saving transitions, and business day scheduling automatically, ensuring reliable recurring automation across different time zones and calendar requirements. **Real-time Event Processing** - Instant flow execution in response to external events through webhooks, platform notifications, and system signals. Enable immediate automation scenarios, notifications, and responsive system behavior. **Platform Integration Monitoring** - Monitor communication platforms like Slack, Teams, and Gmail for messages, mentions, and events. Track file system changes in Google Drive and OneDrive for document processing automation. **Advanced Event Filtering** - Apply conditions and filters to triggers, ensuring flows only execute when specific criteria are met. Support for complex logic, data validation, and contextual decision-making with built-in security and validation. Trigger execution frequency and capabilities may be subject to rate limits imposed by external platforms. Ensure proper error handling and monitoring for robust automation flows. ## Next Steps * Learn about [Inputs & Outputs](/platform/flows/inputs-outputs) * Explore [Flow Design](/core/concepts/flows) * Set up flow [Deployments](/platform/flows/deployments) * Check out the [Platform](/platform/introduction) # Genie Source: https://docs.noxus.ai/platform/genie/overview The in-app Noxus assistant that navigates the platform and builds on it for you **Genie** is the platform-native assistant built into Noxus. It lives in the app itself — not on a separate page — and can both **guide you** through the product and **operate the platform on your behalf**: creating and editing flows, configuring agents, searching knowledge bases, querying tables, and running work in a sandbox. Unlike an [agent](/platform/agents/introduction) that you build and deploy, Genie ships with Noxus and is pre-wired to work against your own workspace. Think of it as a co-worker who already knows the platform and has hands on the same tools you do. Genie is currently in **beta** — the experience keeps improving. Availability is controlled per organization (see [Enablement & permissions](#enablement--permissions)). ## What Genie can do Genie discovers the platform's own tools through the [Noxus MCP server](/sdk/mcp/overview) and can act across the product: Create workflows and edit them node by node, wiring configuration for you as you describe what you want. Read and adjust agent setup — instructions, tools, knowledge — so you can stand up an assistant by conversation. Look things up across your knowledge bases to ground its answers and actions. Query and reason over your workspace tables to answer data questions. Use a persistent [sandbox](/platform/agents/sandbox) shell to process data, run scripts, and produce files. Guide you around the interface and take you to the right place for a task. Genie also plans multi-step work with a todo list and can attach files to the conversation. ## How Genie relates to agents and MCP Genie is a special, hidden [agent](/platform/agents/introduction) that Noxus provisions and manages for you. It reuses the same agent runtime, tools, and sandbox that power the agents you build — the difference is that Genie is created automatically, is scoped to help you operate Noxus, and is configured by the platform rather than in the agent builder. Its platform actions run through the [Noxus MCP server](/sdk/mcp/overview) — the same in-app tool surface the SDK exposes — bound to your workspace with your own permissions. So when Genie edits a flow or reads a knowledge base, it does so as you, subject to the same access rules. Genie binds to the platform's **read and build** tools for flows and keeps them scoped: it uses the flow editing and read tools, but destructive and bulk management operations (such as deleting or running flows outright) are deliberately left out of its toolset. ## Sandbox & approvals Genie's [sandbox](/platform/agents/sandbox) is a **persistent shell workspace** — it stays available across the conversation so Genie can work step by step rather than one command at a time. Genie uses this shell for data and code work instead of one-shot code execution. Because the sandbox can write files and run commands, those actions are **gated behind your approval**: Genie asks before writing files in the sandbox. Genie asks before running shell commands. You stay in control — Genie proposes the step and you approve it before anything runs. As with any agent, running sandbox code also depends on the relevant platform permissions being granted. ## Enablement & permissions * **Organization-gated.** Genie is enabled per organization. Where it isn't enabled, the assistant surface won't appear. * **Managed by org admins.** Organization administrators can open **Settings → Genie** to review and adjust Genie's prompt, model, and tools, just like an agent's configuration. * **Acts as you.** Genie operates with your workspace permissions — it can only reach flows, agents, knowledge bases, and tables that you can, and higher-risk actions (like sandbox execution) still require the corresponding permissions and your in-conversation approval. Genie is a shared, tenant-managed assistant that lives in a hidden system workspace — it doesn't appear in your normal agents list and isn't edited from the agents page. Admins manage it from **Settings → Genie**. # Advanced Settings Source: https://docs.noxus.ai/platform/knowledge-bases/advanced-settings Configure search methods and retrieval settings Fine-tune knowledge base behavior with advanced configuration options. Retrieval Settings ## Search Methods **Uses vector embeddings to understand meaning** | Configuration | Options | Default | | :------------------- | :------------------------------ | :-------- | | Embedding model | FastEmbed, OpenAI, Multilingual | FastEmbed | | Similarity threshold | 0.0 - 1.0 | 0.7 | | Top-K results | 1 - 50 | 5 | **Best For:** Natural language queries, conceptual similarity, finding related information **Example:** "climate initiatives" matches "environmental programs", "sustainability efforts" **Traditional keyword matching with BM25 ranking** | Configuration | Options | Default | | :-------------- | :---------------- | :------------- | | BM25 parameters | k1, b values | k1=1.2, b=0.75 | | Stop words | Enable/disable | Enabled | | Stemming | Language-specific | English | **Best For:** Exact term searches, technical terminology, IDs and codes **Example:** "SKU-12345" requires exact match **Combines semantic + keyword search** | Configuration | Options | Default | | :-------------- | :-------------------- | :------- | | Alpha parameter | 0.0 - 1.0 | 0.5 | | Fusion method | RRF, Relative scoring | RRF | | Reranking | Enable/disable | Disabled | **Recommended:** Start with alpha=0.5 for balanced results between keyword and semantic search **Best For:** Most general-purpose queries with balance between precision and recall **Second-stage ranking for improved precision** **How It Works:** 1. Retrieves larger candidate set (e.g., top 50) 2. Uses ColBERT model to rerank 3. Returns top-K most relevant (e.g., top 5) **Trade-off:** Slower but more accurate. Use when precision is critical. ## Retrieval Settings How many chunks to retrieve: **Recommendations:** * **5-10 chunks**: Most use cases * **3-5 chunks**: Quick answers, cost-sensitive * **10-20 chunks**: Complex questions, comprehensive answers **Trade-offs:** * More chunks = more context but slower and costlier * Fewer chunks = faster but may miss information Filter out low-relevance chunks: **Range:** 0.0 (all results) to 1.0 (exact match only) **Recommendations:** * **0.5-0.6**: Lenient, more results * **0.7**: Balanced, good default * **0.8+**: Strict, high precision Size of document segments: **Options:** * **256 tokens**: Precise, more chunks needed * **512 tokens**: Balanced, recommended default * **1024 tokens**: More context per chunk **Considerations:** * Smaller chunks = more precise but need more retrievals * Larger chunks = more context but less precise Overlap between consecutive chunks: **Typical:** 50-100 tokens **Purpose:** * Prevents information loss at boundaries * Ensures continuity of context * Improves retrieval quality ## Embedding Models | Model | Quality | Speed | Cost | Best For | | :---------------------- | :------ | :---- | :----- | :-------------------------------------- | | **FastEmbed** (Default) | ⭐⭐⭐ | ⚡⚡⚡ | 💰 | Most general use cases | | **OpenAI Embeddings** | ⭐⭐⭐⭐⭐ | ⚡⚡ | 💰💰💰 | Critical applications, maximum accuracy | | **Multilingual Models** | ⭐⭐⭐⭐ | ⚡⚡ | 💰💰 | International documents, 100+ languages | FastEmbed provides the best balance of quality, speed, and cost for most use cases. Upgrade to OpenAI for mission-critical applications or multilingual models for international content. ## Advanced Features Limit search to specific folders: **Benefits:** * Faster searches * More relevant results * Domain-specific retrieval * Organized knowledge **Use Cases:** * Search only "HR Policies" for HR questions * Search only "Product Docs" for technical questions * Separate public vs internal documents Filter by custom metadata fields: **Examples:** * Only documents from "Engineering" department * Only documents tagged "2024" * Only documents by specific author * Only documents of type "Policy" **Configuration:** ```json theme={null} { "metadata_filter": { "department": "Engineering", "year": "2024", "doc_type": "Technical Spec" } } ``` Control how sources are cited: **Options:** * Include page numbers * Show file names * Display chunk IDs * Add custom metadata in citations **Example Output:** ``` Source: Installation Guide.pdf, Page 3 Department: Engineering Last Updated: 2024-01-15 ``` Automate knowledge base operations with flows # Introduction Source: https://docs.noxus.ai/platform/knowledge-bases/introduction Semantic search powered by your documents and data for intelligent AI responses Knowledge bases are collections of documents and data that enable semantic search and retrieval. They transform your content into searchable knowledge that agents and flows can query to provide accurate, contextual information using embeddings, vector search, and intelligent chunking strategies. ## Available Components * [Supported Files](/platform/knowledge-bases/supported-files): Upload PDFs, Word docs, spreadsheets, images, and sync from cloud storage. * [Syncing](/platform/knowledge-bases/syncing): Automatically keep documents in sync with OneDrive, Google Drive, SharePoint, and websites. * [Advanced Settings](/platform/knowledge-bases/advanced-settings): Configure search methods, embedding models, and retrieval settings. * [Managing Through Flows](/platform/knowledge-bases/managing-flows): Automate document ingestion and querying with flow nodes. Each component provides specialized capabilities to build powerful knowledge-driven automation. The following sections explore how to configure and use knowledge bases effectively. # Managing Through Flows Source: https://docs.noxus.ai/platform/knowledge-bases/managing-flows Automate knowledge base operations with flows Use flows to automate knowledge base ingestion, querying, and management. Knowledge Ingestion Flow ## Knowledge Base Nodes Upload documents to knowledge bases from flows. **Use Cases:** * Automated document ingestion * Email attachment processing * Scheduled batch uploads * ETL pipelines **Configuration:** * Knowledge base selection * Target folder * Metadata assignment * Processing options **Example Flow:** ``` Email Trigger → Extract Attachments → Filter PDFs → Knowledge Ingestion Node (upload to "Customer Inquiries") → Send Confirmation Email ``` Query knowledge bases with AI-powered answers. **Use Cases:** * Automated question answering * Document-based decision making * Information extraction * Report generation **Configuration:** * Knowledge base selection * Query input * Folder filter (optional) * Retrieval settings (top-K, threshold) * Model selection **Example Flow:** ``` API Trigger (customer question) → KB Q&A (search Product Docs) → Format Response → Return Answer with Citations ``` Get raw chunks without AI generation. **Use Cases:** * Custom processing of retrieved content * Relevance search only * Building custom RAG pipelines * Advanced analysis workflows **Configuration:** * Knowledge base selection * Search method (semantic, keyword, hybrid) * Number of chunks * Return metadata **Example Flow:** ``` User Query → Knowledge Retriever Node (get relevant chunks) → Custom Analysis Node → Generate Custom Response → Return Result ``` ## Common Patterns **Pattern: Email to Knowledge Base** ``` Email Trigger → Extract Attachments → Filter Documents → Knowledge Ingestion Node → Send Confirmation ``` **Use Case:** Automatically add customer support emails to knowledge base **Configuration:** * Email integration trigger * File type filtering * Folder organization by sender/topic * Notification on completion **Pattern: Process and Index** ``` Scheduled Trigger → Download from Drive → Extract Text → Clean & Format → Knowledge Ingestion Node → Update Index ``` **Use Case:** Daily sync of documents from Google Drive **Configuration:** * Schedule (daily, hourly, etc.) * Drive folder selection * Text cleaning rules * Knowledge base folder mapping **Pattern: Route by Content** ``` New Document → Analyze Content → Condition (Document Type?) → Technical: Upload to Tech KB → Policy: Upload to Policy KB → Marketing: Upload to Marketing KB ``` **Use Case:** Automatically categorize and route documents to appropriate knowledge bases **Configuration:** * Content classification * Multiple knowledge base targets * Folder assignment rules * Metadata tagging **Pattern: Query and Act** ``` User Request → KB Q&A → Condition (Answer Found?) → Yes: Return Answer → No: Escalate to Human ``` **Use Case:** Answer questions from knowledge base, escalate if no answer found **Configuration:** * Confidence threshold * Escalation criteria * Fallback responses * Human notification ## Advanced Workflows Search across multiple knowledge bases: ``` User Query → KB Q&A (Product Docs) → KB Q&A (Support Articles) → KB Q&A (Community Forums) → Synthesize Results → Generate Comprehensive Answer ``` **Use Case:** Comprehensive answers from multiple sources Update knowledge base with new information: ``` Scheduled Trigger (daily) → Fetch New Documents (API) → Check for Duplicates → Knowledge Ingestion Node (new docs only) → Log Update Summary ``` **Use Case:** Keep knowledge base current with latest information ## Integration Examples ``` Slack Message Trigger → Extract Question → KB Q&A (Company Knowledge) → Format for Slack → Post Response → Track Satisfaction ``` ``` New Ticket Trigger → Extract Issue Description → KB Q&A (Support Articles) → If Answer Found: → Post Solution to Ticket → Mark as Resolved → If No Answer: → Assign to Agent → Tag as "Needs Documentation" ``` ``` Content Request → KB Q&A (gather information) → Generate Text (create draft) → KB Q&A (verify facts) → Human Review → Publish Content ``` ## Best Practices * Batch uploads when possible * Use appropriate folder structure * Add meaningful metadata * Validate documents before upload * Test queries with different settings * Monitor retrieval quality * Adjust thresholds based on results * Cache frequent queries * Regular cleanup of outdated documents * Monitor storage usage * Update documents when source changes * Track query performance * Handle upload failures gracefully * Retry with exponential backoff * Log errors for debugging * Notify on critical failures View all supported file formats # Supported Files & Integrations Source: https://docs.noxus.ai/platform/knowledge-bases/supported-files File formats and data sources for knowledge bases Knowledge Bases support a wide range of file formats and can sync content from cloud storage integrations. Knowledge Base Interface ## Supported File Formats | Format | Extensions | Features | | :------------- | :---------- | :------------------------------------------------------------------------------------- | | **PDF** | .pdf | Full text extraction, image processing, page-level tracking, OCR for scanned documents | | **Word** | .docx, .doc | Text and formatting preservation, table extraction | | **Plain Text** | .txt, .md | Direct ingestion, markdown rendering | | **HTML** | .html, .htm | Web pages and formatted content, link preservation | | **Rich Text** | .rtf | Formatted text documents | | Format | Extensions | Features | | :-------- | :---------- | :----------------------------------------------------- | | **Excel** | .xlsx, .xls | Per-sheet processing, table extraction, formula values | | **CSV** | .csv | Tabular data ingestion, header detection | | Format | Extensions | Features | | :------------- | :---------- | :----------------------------------------------------- | | **PowerPoint** | .pptx, .ppt | Slide text extraction, speaker notes, image processing | | Format | Extensions | Features | | :--------------- | :----------------------- | :-------------------------------------------------------------------- | | **Images** | .png, .jpg, .jpeg, .webp | OCR text extraction, vision-based understanding, chart interpretation | | **Scanned PDFs** | .pdf | Automatic OCR or vision model processing | | Format | Extensions | Features | | :-------- | :--------- | :----------------------------------------------------------------------- | | **ZIP** | .zip | Extract and process all contents, maintain folder structure | | **Email** | .eml | Email messages with metadata, attachment processing, thread preservation | ## Cloud Storage Integrations **Features:** * Connect to Google Drive * Select folders to import * Import files from selected folders * Preserve folder structure **Setup:** 1. Connect Google Drive integration 2. Select folders to import 3. Import files into knowledge base Files are imported at the time of selection. They do not automatically sync when updated in Google Drive. **Features:** * Connect to OneDrive or SharePoint * Select document libraries or folders * Import files from selected locations * Preserve folder structure **Setup:** 1. Connect OneDrive/SharePoint integration 2. Select folders or document libraries 3. Import files into knowledge base Files are imported at the time of selection. They do not automatically sync when updated in OneDrive or SharePoint. **Features:** * Crawl entire websites * Follow links automatically * Extract clean text * Preserve page structure * Schedule regular updates **Setup:** 1. Provide starting URL 2. Configure crawl depth 3. Set URL patterns to include/exclude 4. Schedule refresh frequency ## Upload Methods **Via Platform UI:** * Drag-and-drop files * Bulk upload multiple files * Organize into folders * Add metadata tags Supports individual files, multiple files at once, and ZIP archives (auto-extracted) **Programmatic upload via REST API:** ```bash theme={null} curl -X POST https://api.noxus.ai/v1/knowledge-bases/kb_123/documents \ -H "Authorization: Bearer ${API_KEY}" \ -F "file=@document.pdf" \ -F "folder=Product Docs" ``` **Upload via Python SDK:** ```python theme={null} from noxus_sdk.client import Client client = Client(api_key="your_api_key") kb = client.knowledge_bases.get("kb_123") kb.upload_document( file_path="document.pdf", folder="Product Docs", metadata={"author": "John Doe", "version": "2.0"} ) ``` **Automate uploads with flows:** **Use Cases:** * Automate document ingestion * Process email attachments * Sync from external sources * Scheduled batch uploads ## Metadata **Automatically captured:** | Field | Description | | :-------------- | :---------------------- | | Filename | Original file name | | Upload date | When document was added | | File size | Document size in bytes | | Page count | Number of pages (PDFs) | | Folder location | Organization path | **Add your own fields:** **Common Fields:** * Author, Department, Document type * Version, Tags/categories * Creation date, Expiration date **Use Cases:** * Filter searches by metadata * Organize documents * Track document lifecycle * Enable advanced queries Configure search methods and retrieval settings # Syncing Source: https://docs.noxus.ai/platform/knowledge-bases/syncing Automatically keep your knowledge base in sync with external sources like OneDrive, Google Drive, SharePoint, and websites ## Overview Knowledge base syncing automatically detects changes in your external sources and keeps your documents up to date. When files are added, modified, or deleted in the source, Noxus detects the change and updates the knowledge base accordingly. ## Supported Sources | Source | Detection Method | Sync Intervals | | ---------------- | ------------------------------ | --------------------------------- | | **OneDrive** | Full folder listing comparison | Every 5 min – 12 hours | | **Google Drive** | Full folder listing comparison | Every 5 min – 12 hours | | **SharePoint** | Full folder listing comparison | Every 5 min – 12 hours | | **Website** | URL reachability + re-scrape | Daily, Weekly, Bi-weekly, Monthly | ## Enabling Sync There are two ways to enable sync: ### After uploading from an external source When you upload files from OneDrive, Google Drive, or SharePoint, Noxus will offer to enable sync automatically. You can configure the polling interval and whether to delete documents when they're removed from the source. ### From the Syncs tab 1. Navigate to your knowledge base 2. Click the **Syncs** tab 3. Click **Enable Sync** (appears when your KB has documents from a syncable source) 4. Configure the sync interval and deletion behavior 5. Click **Enable Sync** You can also enable sync from the context menu on any synced document — right-click and select **Enable Sync**. ## Managing Syncs The **Syncs** tab shows all active sync triggers for your knowledge base: * **Resource**: The folder or files being watched * **Provider**: The source type (OneDrive, Google Drive, etc.) * **Sync Config**: The polling interval * **Status**: Current sync state (Synced, Waiting, Failed, Paused) * **Actions**: Force sync, edit settings, or stop syncing ### Force Sync Click the play button (▶) to trigger an immediate sync. This bypasses the polling interval and runs the sync workflow right away. The result appears in the **Ingestion Runs** tab. ### Edit Settings Click the pencil button (✏) to change the polling interval or deletion behavior for an existing sync. ### Stop Syncing Click the trash button (🗑) or right-click a synced document and select **Stop Syncing**. This removes the sync trigger. Documents already in the knowledge base are not affected. ## How Changes Are Detected ### File-based sources (OneDrive, Google Drive, SharePoint) On each sync cycle, Noxus lists all files in the watched folder and compares against the known state: * **New files**: Files in the folder that aren't in the KB → downloaded and ingested * **Modified files**: Files with a different `lastModifiedDateTime` → file content updated in-place, document re-ingested (same document ID preserved) * **Deleted files**: Files in the KB that are no longer in the folder → document removed from KB (if deletion is enabled) The first sync establishes a baseline — it records what files exist without making changes. Subsequent syncs detect differences from this baseline. ### Websites Website sync re-scrapes all known URLs on each cycle: * Each page is re-scraped at **depth 0** (single page, no link crawling) * If a URL returns an error (404, connection refused), the document is marked as deleted * The original page title is preserved as the document name Website sync intervals are longer (daily to monthly) since web content changes less frequently than cloud files. ## Sync Status | Status | Meaning | | -------------------------- | --------------------------------------------------------------------- | | **Synced** (green) | Last sync completed successfully. Shows time since last sync. | | **Waiting for first sync** | Sync was just enabled, hasn't run yet. | | **Failed** (red) | Last sync encountered an error. Hover for details. | | **Paused (errors)** (red) | Too many consecutive failures — sync paused. Use Force Sync to retry. | ## Cloud Icon Documents from synced sources show a small blue cloud icon (☁) next to their name in both the file tree and the document table. This indicates the document is being kept in sync with an external source. ## Ingestion Runs Sync operations appear in the **Ingestion Runs** tab alongside regular document ingestion. Each force sync or scheduled sync creates a workflow run that you can inspect for progress, errors, and warnings. # Build with Noxus Source: https://docs.noxus.ai/platform/overview Learn how to build AI automation with flows, agents, and knowledge bases ## Core Building Blocks Visual automation combining AI, data processing, and business logic through connected nodes Conversational AI that uses tools, searches knowledge, and executes flows autonomously Semantic search powered by your documents and data for intelligent AI responses *** ## Building Flows Learn about inputs, outputs, nodes, and triggers that make up flows Explore AI Processing, Data & Files, and Logic & Control nodes Automate flows with schedules, webhooks, and events *** ## Building Agents Write effective agent instructions for consistent, quality results Configure flows, knowledge bases, MCP, and other tools agents can use Deploy agents via platform, API, SDK, or embedded chat widget *** ## Knowledge Bases Upload PDFs, Word docs, spreadsheets, images, and sync from cloud storage Configure search methods, embedding models, and retrieval settings Automate document ingestion and querying with flow nodes *** ## Common Scenarios Use AI nodes and agents for text generation, vision, and intelligent processing Process, analyze, and automate document workflows Build advanced flows with conditionals, loops, and sophisticated control flow Combine agent reasoning with structured flow automation *** ## Learning Resources New to Noxus? Start here to understand the platform fundamentals Navigate the docs efficiently with role-based guides and quick links Step-by-step video guides for building flows and agents (coming soon) Pre-built flow templates for common use cases (coming soon) Ask questions and share knowledge with other Noxus builders Learn proven patterns for building reliable, efficient automation *** ## Quick Start Paths Understand [what flows are](/platform/concepts/flows) and when to use them Learn about [nodes, inputs, outputs, and triggers](/platform/flows/introduction) Follow a [common scenario](/platform/scenarios/working-with-ai) to build something real Connect [external services](/platform/flows/integrations) to your flows Understand [what agents are](/platform/concepts/agents) and their capabilities Learn to write effective [prompts and instructions](/platform/agents/prompts-instructions) Give your agent [access to tools](/platform/agents/tools) like flows and knowledge bases Choose your [deployment method](/platform/agents/deployments) (platform, API, SDK, widget) Understand [what knowledge bases are](/platform/concepts/knowledge-bases) and how they work Learn about [supported file formats](/platform/knowledge-bases/supported-files) and upload methods Set up [advanced search settings](/platform/knowledge-bases/advanced-settings) for optimal retrieval Integrate KBs into [flows](/platform/knowledge-bases/managing-flows) or [agent tools](/platform/agents/tools) *** ## Need Help? Get help from our team with any questions See Noxus in action with a guided demo # Data Tables Source: https://docs.noxus.ai/platform/tables/overview Structured, spreadsheet-like data your flows and agents can read and query **Data Tables** give each workspace structured, SQL-backed storage — rows and typed columns you manage like a spreadsheet, but that your flows and agents can read, write, and query with SQL. Use them to stage reference data, capture flow output, or give an agent a source of truth to look things up in. ## Concepts * **Table** — a named collection of rows with a fixed set of **columns**. Every table has an implicit `id` primary key. * **Column types** — `string`, `number`, `boolean`, `datetime`, and `file`. * **Rows** — records keyed by `id`. The `id` is a UUID by default, or an auto-incrementing integer if you choose the `serial` id type at creation. * **Workspace-scoped** — tables belong to a workspace; access follows your workspace role. Alongside your own tables, the platform exposes read-only **platform views** (workflows, runs, users, agents, conversations, knowledge bases) you can query the same way. ## Working with tables Create tables, add columns, and edit rows in a spreadsheet-style UI. Import from CSV to bootstrap a table, and export back to CSV. Read from and write to tables inside a flow, and run SQL with the query node — e.g. look up a record mid-flow or persist results. Give an agent the **Data Tables** or **SQL** tool so it can answer questions from your data with read-only queries. See [Agent Tools](/platform/agents/tools). Full CRUD, bulk insert, CSV import/export, and SQL via the [SDK](/sdk/resources/tables) and [REST API](/api-reference/v1--tables/list-tables). ## SQL queries Query across the workspace's tables with **read-only SQL**. Queries are guarded server-side (read-only, scoped to your workspace's data) and reference a table by its `sql_name` (the lowercased table name): ```sql theme={null} SELECT email, signups FROM customers WHERE active ``` ## Next steps Create tables, manage columns and rows, run SQL, import/export CSV. The full REST reference for the tables endpoints. # Evaluators Source: https://docs.noxus.ai/platform/tests/evaluators All available evaluator types for testing flow outputs Evaluators are the rules that score your test case outputs. Each evaluator targets a specific output field from your flow and returns a pass/fail result with optional feedback. You can attach multiple evaluators to a single test. When a case runs, every evaluator is applied independently, and the case passes only if **all** evaluators pass. Evaluator list ## Output Field Targeting Every evaluator requires you to select an **output field** -- the specific output connector from your flow that the evaluator will inspect. Only text outputs can be evaluated at this time. Non-text outputs (files, images, etc.) will not appear in the output field selector. ## Per-Case Overrides Some evaluator settings act as **defaults** that can be overridden directly on individual test cases. This lets you reuse a single evaluator across many cases while customizing the expected value for each one. When you open a case, overridable properties appear in the **Evaluator values** section of the case editor. For example, an `Equals` evaluator might have a default expected value of `"Hello"`, but for a specific case you can override it to `"Goodbye"` without creating a separate evaluator. Properties that support per-case overrides are marked with in the tables below. ### Using Dynamic Inputs and Outputs Some properties also support **dynamic references** to your flow's inputs and outputs. These can be inserted in a compatible field using `/` or the `Insert variable` option. They will be represented as chips. * **Input** -- corresponds to the value of a flow input, as mapped in the case. Useful when the expected output should match or contain the original input. * **Output** -- resolves to the actual value produced by the flow when the case is run. Useful when comparing one output against another. This makes it possible to write evaluators like "the summary output should contain the customer name from the input" without hardcoding values. The Inputs and Outputs values are case dependent. Input/output references ## Deterministic Evaluators These evaluators apply rule-based checks. They run instantly, produce consistent results, and do not consume model tokens. ### Regex Matches the output against a regular expression pattern. | Setting | Description | Default | | -------------- | ----------------------------------------------------------- | ------- | | **Pattern** | The regex pattern to match. | — | | **Full match** | If enabled, the entire output must match the regex pattern. | Off | ### Is JSON Validates that the output is a well-formed JSON object. | Setting | Description | Default | | ---------- | ------------------------------------------------------------------ | ------- | | **Strict** | Enforce official JSON specifications while reading and validating. | On | ### Starts With Checks whether the output begins with a given prefix. | Setting | Description | Default | | --------------------------------- | ------------------------------------------------------------------------- | ------- | | **Prefix** | The string the output must start with. | — | | **Case sensitive** | Whether uppercase and lowercase letters are treated as different (A ≠ a). | Off | ### Does Not Start With Verifies the output does not begin with a given prefix. | Setting | Description | Default | | --------------------------------- | ------------------------------------------------------------------------- | ------- | | **Prefix** | The string the output must not start with. | — | | **Case sensitive** | Whether uppercase and lowercase letters are treated as different (A ≠ a). | Off | ### Contains Checks whether the output contains one or more substrings. | Setting | Description | Default | | ------------------------------------- | ------------------------------------------------------------------------- | ------- | | **Substrings** | List of strings to search for in the output. | — | | **Case sensitive** | Whether uppercase and lowercase letters are treated as different (A ≠ a). | Off | | **Require all** | Every substring must be present for the evaluator to pass. | Off | ### Does Not Contain The inverse of Contains -- verifies that certain substrings are absent from the output. | Setting | Description | Default | | ------------------------------------- | ------------------------------------------------------------------------- | ------- | | **Substrings** | List of strings that should not appear in the output. | — | | **Case sensitive** | Whether uppercase and lowercase letters are treated as different (A ≠ a). | Off | | **Require all** | None of the substrings can be present for the evaluator to pass. | Off | ### Equals Checks whether the output exactly matches an expected string. | Setting | Description | Default | | ----------------------------------------- | ------------------------------------------------------------------------- | ------- | | **Expected value** | The string the output must match. | — | | **Case sensitive** | Whether uppercase and lowercase letters are treated as different (A ≠ a). | Off | | **Strip whitespace** | Remove leading and trailing whitespace before comparing. | On | ### Similar (Sequence Matcher) Compares the output to an expected string using sequence-based similarity scoring. | Setting | Description | Default | | ----------------------------------------- | ------------------------------------------------------------------------------------- | ------- | | **Expected value** | The reference string to compare against. | — | | **Threshold** | Minimum score required for this evaluation to pass (0.0 = no match, 1.0 = identical). | 0.8 | | **Case sensitive** | Whether uppercase and lowercase letters are treated as different (A ≠ a). | Off | **Sequence-based similarity** — A method that compares the longest contiguous matching subsequences between the output and the expected value. A score of 1.0 means the strings are identical, while 0.0 means they share no common sequences. ## AI Evaluators AI evaluators use an LLM to assess outputs against natural language criteria. They are more flexible than deterministic evaluators but consume model tokens and may produce slightly different results across runs. ### Rule-Based (LLM) Evaluates the output against free-text rules using an LLM judge. The LLM receives the test case input, the flow output, and your rules. It then assigns a score from 1 to 10 based on how well the output aligns with the rules. The score is compared against your pass threshold to determine if the evaluation passes. | Setting | Description | Default | | ------------------ | ---------------------------------------------------------------------------------------------------------------------- | ------- | | **Rules** | Natural language instructions describing what makes a good output (e.g., "The response should be polite and concise"). | — | | **Pass threshold** | Minimum score required for this evaluation to pass. | 7 | | **Model** | The LLM model to use for evaluation. | — | ### Criteria-Based (LLM) Evaluates the output against multiple named criteria using an LLM judge. The LLM scores each criterion independently, giving it a score from 1 to 10 based on how well the output aligns with it. The average of the final scores is compared against the pass threshold. This is useful when you want to assess different quality dimensions separately -- for example, accuracy, tone, and completeness. | Setting | Description | Default | | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | | **Criteria** | A list of named criteria, each with its own instructions (e.g., name: "Tone", instructions: "The tone in the response should be formal but witty."). | — | | **Pass threshold** | Minimum average score required for this evaluation to pass. | 7 | | **Model** | The LLM model to use for evaluation. | — | ## Default Behavior When an evaluator's key value is left empty (e.g., an `Equals` evaluator with no expected value, or a `Rule-Based` evaluator with no rules), the evaluator **passes by default**. This means you can add evaluators to a test and configure their values incrementally per case without unconfigured evaluators causing failures. ## Combining Evaluators A test can use any number of evaluators. A case passes only when **every** evaluator passes. This lets you layer checks -- for example: * An **Is JSON** evaluator to verify the output is valid JSON. * A **Contains** evaluator to check for required fields. * A **Rule-Based (LLM)** evaluator to assess the quality of the content. If any evaluator fails, the case is marked as failed, and the specific failing evaluator is highlighted in the results. # Introduction Source: https://docs.noxus.ai/platform/tests/introduction Automated testing and quality assurance for your flows Tests (or Evaluations) let you define repeatable tests that verify your flows produce correct, high-quality outputs. By combining **tests**, **evaluators**, and **cases**, you can catch regressions, compare versions, and build confidence before deploying changes. Tests overview ## Key Concepts ### Tests A test is a named collection of evaluators and cases scoped to a single flow. Each test acts as an independent group that can be run on its own or together with other tests. A test contains: * **Evaluators** -- rules or criteria that score each case's output. * **Cases** -- specific input scenarios to run against your flow. ### Evaluators Evaluators are the scoring functions applied to each case's output. They determine whether the output is correct by returning a **pass/fail** result. Only text outputs can be evaluated at this time. File outputs, images, and other non-text types are not supported by evaluators. Noxus provides two categories of evaluators: * **Deterministic evaluators** -- rule-based checks like regex matching, string comparison, and JSON validation. Fast, predictable, and free. * **AI evaluators** -- LLM-powered assessments that score outputs against natural language rules or multi-criteria rubrics. Flexible but consume model tokens. See [Evaluators](/platform/tests/evaluators) for the full list of available evaluators and their configuration options. ### Cases A case defines the inputs your flow will receive during an evaluation run. Cases can optionally include **Evaluator Values** -- parameters that an evaluator needs to perform its check. You can override some of these values per case when a specific scenario requires different criteria. For example, the `Equals` evaluator requires a target value to compare against, and that target may differ from one case to another. You can create cases manually or generate them from a previous successful run. See [Cases](/platform/tests/test-cases) for details on creating and managing test cases. ## How It Works 1. **Create a test** for your flow. 2. **Add evaluators** that define what "correct" means -- string matches, JSON validation, LLM-based scoring, or any combination. 3. **Add cases** with the inputs you want to verify. 4. **Run the test** against the current version or a specific version of your flow. 5. **Review results** -- each case shows a pass/fail status per evaluator, an overall score, and detailed feedback. When your flow definition changes after a run, results are automatically flagged as **outdated** so you know to re-run. Running a test ## Score Ring Each test displays a score ring summarizing the latest results at a glance: * **Green** -- passed cases * **Red** -- failed cases (evaluator assertions did not pass) * **Gray** -- cases not yet run, or cases that encountered an execution error The percentage shown is the overall pass rate across all cases. Score ring ## Statuses Cases can have the following statuses: | Status | Meaning | | -------------------------------------------------- | ------------------------------------------------------------------------------------- | | Passed | All evaluators passed for this case. | | Failed | One or more evaluators did not pass. | | Error | The flow failed to execute before evaluators could run. | | Running | The evaluation is currently in progress. | | Not run | No evaluation results exist for this case yet. | | Cancelled | The evaluation run was cancelled by a user. | | | Results exist but the flow, evaluators, or case data have changed since the last run. | ## Next Steps * [Evaluators](/platform/tests/evaluators) -- explore all available evaluator types and their configuration. * [Cases](/platform/tests/test-cases) -- learn how to create and manage test cases. * [Running Tests](/platform/tests/running-tests) -- understand how to run evaluations and interpret results. # Running Tests Source: https://docs.noxus.ai/platform/tests/running-tests Executing evaluations and interpreting results Once your test has evaluators and cases, you can run evaluations to verify your flow's behavior. ## Running Evaluations Each case runs your flow independently with its defined inputs, then applies every evaluator against the outputs. Results are surfaced both as an at-a-glance summary on the test level and as detailed breakdowns per case. ### How to Run By default, evaluations run against the current version of your flow. Use the **version selector** to target a specific saved version instead. Once the version is selected, you can: * **Run all tests at once** -- click **Run tests** from the tests list for full regression testing. * **Run all cases in a test** -- use the button on a test row or click **Run all** from the test detail view. * **Run individual cases** -- use the button on a case row or the **Run** button inside a case. You can **cancel** running evaluations at any time with the **Cancel all** button or the stop button on individual cases. Evaluation results are not versioned. Switching to a different flow version will mark all existing results as outdated, and cases may fail to run if the version changes the flow's inputs. ## Understanding Results ### Test Summary The summary bar at the top of each test shows the **pass rate**, **average latency**, **last run time**, and **token cost**. Its background color reflects the overall state: gray (running or not all run), green (all passed), red (any failed), or yellow (any outdated). Test summary ### Per-Case Results Click on a case to open a detailed view. The left panel shows the case inputs and evaluator values, while the right panel displays the run output and each evaluator's pass/fail status with feedback. You can compare previous runs using the run history dropdown. The right panel also includes full run details for quick debugging — admins have access to execution logs as well. Case results ### Execution Errors When a flow fails to execute, the case is marked with **Error** -- distinct from **Failed**, which means the flow completed but evaluators didn't pass. A warning banner appears at the top of the test when execution errors are detected. ## Outdated Results Noxus tracks changes using content hashes. Results are flagged as **outdated** when any of the following change after a run: | Change | Meaning | | -------------------- | ------------------------------------------------------- | | **Flow definition** | The workflow logic was modified. | | **Evaluator config** | An evaluator's settings were changed. | | **Case data** | The test case inputs or expected outputs were modified. | | **Evaluator set** | Evaluators were added to or removed from the test. | Outdated results display a yellow warning triangle and a banner. Click **Run outdated tests** to re-evaluate only the affected tests. ## Testing Flows with Integrations Each test case triggers an actual workflow run — every node in the flow executes for real. This means integration nodes that perform external actions (sending emails, posting Slack messages, creating tickets) **will fire on every test run**. ### Use Subflows to Isolate Logic To test a flow that ends with an integration action, extract the logic you want to validate into a **subflow** that stops before the integration node. Run your tests against the subflow instead of the full flow. For example, if your flow generates an email body and then sends it via Gmail, create a subflow that only covers the generation step. Your evaluators can then assert on the email content without actually sending anything. Subflow testing also makes cases faster and cheaper to run, since you skip external API calls and their associated latency. ### Integration Nodes as Inputs Integration nodes that read external data (Slack messages, emails) can be useful as test inputs. However, this data might change between runs and may produce inconsistent results. Prefer static test case inputs for reliable evaluations. Reserve live-data inputs for exploratory or smoke-style tests where you accept variability. ## Best Practices * **Start with deterministic evaluators** -- fast, free, and predictable. Add AI evaluators only for subjective quality assessment. * **Use specific output fields** -- target individual output connectors for precise assertions. * **Name cases descriptively** -- "Long input with special characters" is easier to debug than "Test 1". * **Run before deploying a version** -- treat evaluations like a CI pipeline. * **Combine evaluator types** -- layer structural checks (Is JSON, Contains) with quality checks (Rule-Based LLM). * **Keep cases focused** -- one behavior per case makes failures easier to pinpoint. * **Prefer subflow testing** -- break complex flows into testable subflows. This avoids triggering integration side effects, reduces test complexity, and makes failures easier to trace. # Cases Source: https://docs.noxus.ai/platform/tests/test-cases Creating and managing test cases for your evaluations Test cases define the inputs your flow receives during an evaluation run. Each case represents a specific scenario you want to verify. ## Creating Cases A user can choose to create a case from scratch or from a previous successful run. ### From Scratch Navigate to the test where you want to add a case and click **Add case**. Give the case a descriptive label (e.g., "Short product description", "Edge case: empty input"). Provide values for each of the flow's input connectors. The form automatically shows the input fields defined in your flow. Provide the values needed for each evaluator to perform its check. They may come with a default set on the evaluator, but can be overridden per case to match the expected result for that specific scenario. See [Per-Case Overrides](/platform/tests/evaluators#per-case-overrides) for details. Case editor ### From a Previous Run Navigate to the **Past runs** tab of your flow. Find a completed run that represents a good test scenario and click the icon on that row. This will pre-fill the case inputs with the values used in that run. Choose which test to add the case to, or create a new one, and save. You can also reach the **Past runs** from inside a test — click the dropdown next to **Add case** and select **Import from runs**. ### Managing Inputs Test case inputs map directly to your flow's input connectors. When your flow has multiple inputs (e.g., a text input and a file input), each one appears as a separate field in the case editor. Inputs marked as **not visible when running** (fixed inputs with the "Visible when running" toggle disabled) are automatically hidden from the case editor. The flow uses their fixed value during execution -- you do not need to provide them. If a fixed input is still visible when running, its value is pre-filled but can be overridden per case. If you modify your flow's inputs after creating cases, existing cases may become **invalid** -- the evaluation will flag them with an error status so you can update them. ### Evaluator Overrides Some evaluators have properties that can be **overridden per case**. The value you set on the evaluator acts as the default, but you can customize it for individual cases without creating a separate evaluator. Overridable properties appear in the **Evaluator overrides** section at the bottom of the case editor. Only properties marked as overridable on the evaluator are shown -- see the [Evaluators](/platform/tests/evaluators) page for which properties support this. ### Bulk Operations You can select multiple cases using the checkboxes in the cases table to perform bulk actions: * **Run selected** -- execute evaluations only for the selected cases. * **Delete selected** -- remove multiple cases at once. ## Running Cases You can run cases in several ways: * **Run all** -- from the test detail view, click **Run all** to execute every case in the test at once. * **Run from the table** -- click the button on a specific case row to run just that case. * **Run from inside a case** -- open a case and click **Run** to execute it individually and see the results in real time. * **Run a selection** -- select multiple cases using the checkboxes and use the **Run** bulk action. You must have at least one evaluator configured in the test before you can run any cases. The run buttons will be disabled until an evaluator is added. To learn more about running evaluations, interpreting results, and handling errors, see [Running Tests](/platform/tests/running-tests). ### Status Indicators Each case in the table shows its current status: | Status | Meaning | | -------------------------------------------------- | -------------------------------------------------------------------------------------- | | Passed | All evaluators passed. | | Failed | One or more evaluators failed. | | Error | The flow failed to execute before evaluators could run. | | Running | Evaluation is in progress. | | Not run | No results exist yet. | | Cancelled | The run was cancelled. | | | Results are outdated -- the flow, evaluators, or case data changed since the last run. | # Variables & Secrets Source: https://docs.noxus.ai/platform/variables/overview Reusable configuration and credentials for your flows and agents **Variables** let you store configuration once and reuse it across flows and agents instead of hardcoding values. **Secrets** are variables whose value is write-only — you can set and reference them, but they're never shown again after saving, so API keys and tokens stay safe. ## Variables vs. secrets | | Variable | Secret | | ------------------ | ------------------------------------------ | ---------------------------------------------- | | **Purpose** | Non-sensitive config (URLs, IDs, defaults) | Sensitive values (API keys, tokens, passwords) | | **Readable back?** | Yes | No — write-only; the value is redacted on read | | **Typical use** | Environment/config a flow reads | Credentials a node or integration needs | Values are **typed** — `string`, `number`, `boolean`, `json`, `datetime`, or `file` — so a flow gets the right shape when it reads one. ## Scopes * **Workspace variables** — shared across everything in a workspace. Manage them in workspace settings. * **Workflow variables** — scoped to a single flow, for values that only that flow needs. ## Using them Reference a variable from a node's config instead of typing a literal, so one change updates every flow that uses it. Store an API key as a **secret**, then reference it where a node or integration needs it — the value never appears in the flow definition. List, create, update, and delete variables via the [SDK](/sdk/resources/admin) and [REST API](/api-reference/v1--variables/list-variables). Secret values are stripped from responses. Because secret values can't be read back, keep the source of truth for a credential wherever you generated it. To rotate, set a new value on the secret. # API Reference Introduction Source: https://docs.noxus.ai/sdk/api-reference/introduction Complete API reference for the Noxus Client SDK ## Overview The Noxus Client SDK provides a comprehensive Python interface to the Noxus AI platform. This API reference covers all classes, methods, and configuration options available in the SDK. ## SDK Structure The SDK is organized into several main modules: Main entry point for all SDK operations Service classes for different platform features Workflow definition and management Data models and type definitions ## Import Structure ```python theme={null} # Main client from noxus_sdk.client import Client # Resource services from noxus_sdk.resources.conversations import ( ConversationService, ConversationSettings, MessageRequest ) from noxus_sdk.resources.workflows import WorkflowService from noxus_sdk.resources.knowledge_bases import KnowledgeBaseService from noxus_sdk.resources.assistants import AgentService # Workflow building from noxus_sdk.workflows import WorkflowDefinition # Tools and utilities from noxus_sdk.resources.conversations import ( WebResearchTool, KnowledgeBaseQaTool, WorkflowTool ) ``` ## Common Patterns ### Error Handling All SDK methods can raise HTTP exceptions: ```python theme={null} import httpx from noxus_sdk.client import Client try: client = Client(api_key="your_key") workflows = client.workflows.list() except httpx.HTTPStatusError as e: if e.response.status_code == 401: print("Authentication failed") elif e.response.status_code == 403: print("Access denied") else: print(f"HTTP error: {e.response.status_code}") except httpx.RequestError as e: print(f"Network error: {e}") ``` ### Async Operations Most methods have async counterparts prefixed with `a`: ```python theme={null} import asyncio async def async_example(): client = Client(api_key="your_key") # Sync version workflows = client.workflows.list() # Async version workflows = await client.workflows.alist() return workflows workflows = asyncio.run(async_example()) ``` ### Pagination List methods support pagination: ```python theme={null} # Get first page page1 = client.workflows.list(page=1, page_size=10) # Get specific page page2 = client.workflows.list(page=2, page_size=10) # Iterate through all pages all_workflows = [] page = 1 while True: workflows = client.workflows.list(page=page, page_size=50) if not workflows: break all_workflows.extend(workflows) page += 1 ``` ## Type Hints The SDK uses comprehensive type hints for better development experience: ```python theme={null} from typing import List, Optional from noxus_sdk.client import Client from noxus_sdk.resources.workflows import Workflow def get_workflow_by_name(client: Client, name: str) -> Optional[Workflow]: workflows: List[Workflow] = client.workflows.list() for workflow in workflows: if workflow.name == name: return workflow return None ``` ## Configuration ### Environment Variables The SDK respects these environment variables: * `NOXUS_API_KEY` - Your API key * `NOXUS_BACKEND_URL` - Custom backend URL ### Client Configuration ```python theme={null} client = Client( api_key="your_key", base_url="https://backend.noxus.ai", load_nodes=True, load_me=True, extra_headers={"Custom-Header": "value"} ) ``` ## Response Models All API responses are returned as Pydantic models with full type safety: ```python theme={null} # Workflow response workflow = client.workflows.get("workflow_id") print(workflow.id) # str print(workflow.name) # str print(workflow.created_at) # datetime print(workflow.nodes) # List[WorkflowNode] # Conversation response conversation = client.conversations.get("conv_id") print(conversation.id) # str print(conversation.settings) # ConversationSettings ``` ## Rate Limits and Retries The SDK handles rate limits automatically with exponential backoff: ```python theme={null} # The client automatically retries on rate limits # You can configure retry behavior in the client client = Client( api_key="your_key", # Retry configuration is handled internally ) ``` ## Debugging Enable debug logging to see HTTP requests: ```python theme={null} import logging # Enable debug logging logging.basicConfig(level=logging.DEBUG) logger = logging.getLogger("noxus_sdk") # Now all HTTP requests will be logged client = Client(api_key="your_key") workflows = client.workflows.list() ``` ## Next Steps Complete client API reference Workflow management API reference Conversation management API reference Knowledge base management API reference # Async Operations Source: https://docs.noxus.ai/sdk/concepts/async-operations Learn how to use asynchronous operations for better performance in your Noxus applications ## Overview The Noxus Client SDK provides comprehensive support for asynchronous operations, allowing you to build high-performance applications that can handle multiple concurrent requests efficiently. This is particularly useful for I/O-bound operations like API calls, file uploads, and long-running workflows. ## Why Use Async? Handle multiple operations concurrently without blocking Use system resources more efficiently with non-blocking I/O Build applications that can handle more concurrent users Keep your application responsive during long operations ## Async Method Pattern All async methods in the SDK follow a consistent naming pattern - they're prefixed with `a`: ```python theme={null} # Synchronous methods workflows = client.workflows.list() workflow = client.workflows.get("workflow_id") conversation = client.conversations.create(name="Test", settings=settings) # Asynchronous equivalents workflows = await client.workflows.alist() workflow = await client.workflows.aget("workflow_id") conversation = await client.conversations.acreate(name="Test", settings=settings) ``` ## Basic Async Usage ### Simple Async Function ```python theme={null} import asyncio from noxus_sdk.client import Client async def main(): client = Client(api_key="your_api_key_here") # Async operations workflows = await client.workflows.alist() models = await client.aget_models() print(f"Found {len(workflows)} workflows") print(f"Available models: {len(models)}") # Run the async function asyncio.run(main()) ``` ### Async Context Manager For better resource management: ```python theme={null} import asyncio import aiohttp from noxus_sdk.client import Client async def main(): client = Client(api_key="your_api_key_here") try: # Perform async operations workflows = await client.workflows.alist() for workflow in workflows[:3]: # Process first 3 details = await client.workflows.aget(workflow.id) print(f"Workflow: {details.name}") finally: # Cleanup if needed pass asyncio.run(main()) ``` ## Concurrent Operations ### Running Multiple Operations Concurrently Use `asyncio.gather()` to run multiple operations simultaneously: ```python theme={null} import asyncio from noxus_sdk.client import Client async def get_platform_info(): client = Client(api_key="your_api_key_here") # Run multiple operations concurrently workflows_task = client.workflows.alist() models_task = client.aget_models() presets_task = client.aget_chat_presets() nodes_task = client.aget_nodes() # Wait for all to complete workflows, models, presets, nodes = await asyncio.gather( workflows_task, models_task, presets_task, nodes_task ) return { "workflows": len(workflows), "models": len(models), "presets": len(presets), "nodes": len(nodes) } # Usage info = asyncio.run(get_platform_info()) print(info) ``` ### Processing Collections Concurrently Process multiple items concurrently with controlled concurrency: ```python theme={null} import asyncio from noxus_sdk.client import Client async def process_workflow(client, workflow_id): """Process a single workflow""" workflow = await client.workflows.aget(workflow_id) runs = await client.runs.alist(workflow_id=workflow_id) return { "id": workflow.id, "name": workflow.name, "run_count": len(runs) } async def process_all_workflows(): client = Client(api_key="your_api_key_here") # Get all workflows workflows = await client.workflows.alist() # Process workflows concurrently (limit concurrency to 5) semaphore = asyncio.Semaphore(5) async def process_with_semaphore(workflow_id): async with semaphore: return await process_workflow(client, workflow_id) # Create tasks for all workflows tasks = [ process_with_semaphore(workflow.id) for workflow in workflows ] # Wait for all to complete results = await asyncio.gather(*tasks) return results # Usage results = asyncio.run(process_all_workflows()) for result in results: print(f"{result['name']}: {result['run_count']} runs") ``` ## Async Workflows ### Creating and Running Workflows Asynchronously ```python theme={null} import asyncio from noxus_sdk.client import Client from noxus_sdk.workflows import WorkflowDefinition async def create_and_run_workflow(): client = Client(api_key="your_api_key_here") # Create workflow definition workflow_def = WorkflowDefinition(name="Async Workflow") input_node = workflow_def.node("InputNode").config( label="Input", fixed_value=True, value="Tell me about async programming", type="str" ) ai_node = workflow_def.node("TextGenerationNode").config( template="Explain: ((Input 1))", model=["gpt-4o-mini"] ) output_node = workflow_def.node("OutputNode") # Connect nodes workflow_def.link(input_node.output(), ai_node.input("variables", "Input 1")) workflow_def.link(ai_node.output(), output_node.input()) # Save workflow asynchronously workflow = await client.workflows.asave(workflow_def) print(f"Created workflow: {workflow.id}") # Run workflow asynchronously run = await workflow.arun(body={}) # Wait for completion asynchronously result = await run.a_wait(interval=2) return result # Usage result = asyncio.run(create_and_run_workflow()) print(f"Result: {result.output}") ``` ## Async Conversations ### Handling Multiple Conversations ```python theme={null} import asyncio from noxus_sdk.client import Client from noxus_sdk.resources.conversations import ConversationSettings, MessageRequest async def handle_multiple_conversations(): client = Client(api_key="your_api_key_here") settings = ConversationSettings( model=["gpt-4o-mini"], temperature=0.7, max_tokens=150 ) # Create multiple conversations concurrently conversation_tasks = [ client.conversations.acreate( name=f"Conversation {i}", settings=settings ) for i in range(3) ] conversations = await asyncio.gather(*conversation_tasks) # Send messages to all conversations concurrently message_tasks = [] for i, conv in enumerate(conversations): message = MessageRequest(content=f"Hello from conversation {i}!") message_tasks.append(conv.aadd_message(message)) responses = await asyncio.gather(*message_tasks) # Print responses for i, response in enumerate(responses): print(f"Conversation {i}: {response.message_parts}") return conversations # Usage conversations = asyncio.run(handle_multiple_conversations()) ``` ## Async Knowledge Bases ### Document Processing with Async ```python theme={null} import asyncio from noxus_sdk.client import Client from noxus_sdk.resources.knowledge_bases import KnowledgeBaseSettings async def process_knowledge_base(): client = Client(api_key="your_api_key_here") # Create knowledge base kb = await client.knowledge_bases.acreate( name="Async KB", description="Created asynchronously", document_types=["pdf", "txt"] ) # Upload multiple documents concurrently files = ["doc1.txt", "doc2.txt", "doc3.txt"] upload_tasks = [ kb.aupload_document(files=[file], prefix=f"/docs/{file}") for file in files ] run_ids = await asyncio.gather(*upload_tasks) print(f"Started {len(run_ids)} upload processes") # Monitor processing status while True: kb = await kb.arefresh() if kb.status == "ready": break print(f"KB status: {kb.status}") await asyncio.sleep(5) print("Knowledge base ready!") return kb # Usage kb = asyncio.run(process_knowledge_base()) ``` ## Error Handling in Async Code ### Handling Individual Errors ```python theme={null} import asyncio import httpx from noxus_sdk.client import Client async def safe_async_operation(): client = Client(api_key="your_api_key_here") try: workflows = await client.workflows.alist() return workflows except httpx.HTTPStatusError as e: print(f"HTTP error: {e.response.status_code}") return [] except httpx.RequestError as e: print(f"Network error: {e}") return [] except Exception as e: print(f"Unexpected error: {e}") return [] # Usage workflows = asyncio.run(safe_async_operation()) ``` ### Handling Errors in Concurrent Operations ```python theme={null} import asyncio import httpx from noxus_sdk.client import Client async def fetch_workflow_safe(client, workflow_id): """Safely fetch a workflow with error handling""" try: return await client.workflows.aget(workflow_id) except httpx.HTTPStatusError as e: print(f"Failed to fetch workflow {workflow_id}: {e.response.status_code}") return None except Exception as e: print(f"Error fetching workflow {workflow_id}: {e}") return None async def fetch_multiple_workflows_safe(): client = Client(api_key="your_api_key_here") # Get workflow IDs workflows = await client.workflows.alist() workflow_ids = [w.id for w in workflows[:5]] # First 5 # Fetch details concurrently with error handling tasks = [ fetch_workflow_safe(client, workflow_id) for workflow_id in workflow_ids ] results = await asyncio.gather(*tasks, return_exceptions=True) # Filter out None results and exceptions successful_results = [ result for result in results if result is not None and not isinstance(result, Exception) ] print(f"Successfully fetched {len(successful_results)} workflows") return successful_results # Usage workflows = asyncio.run(fetch_multiple_workflows_safe()) ``` ## Performance Tips Control concurrency to avoid overwhelming the API: ```python theme={null} async def rate_limited_operations(): semaphore = asyncio.Semaphore(5) # Max 5 concurrent operations async def limited_operation(item): async with semaphore: return await process_item(item) tasks = [limited_operation(item) for item in items] return await asyncio.gather(*tasks) ``` Group related operations to reduce API calls: ```python theme={null} async def batch_workflow_creation(): # Create multiple workflows in one batch workflow_defs = [create_workflow_def(i) for i in range(10)] # Process in batches of 3 batch_size = 3 results = [] for i in range(0, len(workflow_defs), batch_size): batch = workflow_defs[i:i + batch_size] batch_tasks = [client.workflows.asave(wf) for wf in batch] batch_results = await asyncio.gather(*batch_tasks) results.extend(batch_results) # Small delay between batches await asyncio.sleep(0.1) return results ``` Process results as they become available: ```python theme={null} async def process_as_completed(): client = Client(api_key="your_api_key_here") # Create tasks tasks = [ client.workflows.aget(workflow_id) for workflow_id in workflow_ids ] # Process results as they complete for coro in asyncio.as_completed(tasks): try: workflow = await coro print(f"Processed: {workflow.name}") # Do something with the workflow immediately except Exception as e: print(f"Error: {e}") ``` ## Integration with Web Frameworks ### FastAPI Integration ```python theme={null} from fastapi import FastAPI, HTTPException from noxus_sdk.client import Client import os app = FastAPI() client = Client(api_key=os.getenv("NOXUS_API_KEY")) @app.get("/workflows") async def list_workflows(): try: workflows = await client.workflows.alist() return {"workflows": [{"id": w.id, "name": w.name} for w in workflows]} except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @app.post("/workflows/{workflow_id}/run") async def run_workflow(workflow_id: str, input_data: dict): try: workflow = await client.workflows.aget(workflow_id) run = await workflow.arun(body=input_data) result = await run.a_wait(interval=2) return {"result": result.output} except Exception as e: raise HTTPException(status_code=500, detail=str(e)) ``` ### Django Async Views ```python theme={null} from django.http import JsonResponse from django.views.decorators.http import require_http_methods from asgiref.sync import sync_to_async from noxus_sdk.client import Client import os client = Client(api_key=os.getenv("NOXUS_API_KEY")) @require_http_methods(["GET"]) async def list_workflows(request): try: workflows = await client.workflows.alist() data = {"workflows": [{"id": w.id, "name": w.name} for w in workflows]} return JsonResponse(data) except Exception as e: return JsonResponse({"error": str(e)}, status=500) ``` ## Testing Async Code ### Using pytest-asyncio ```python theme={null} import pytest import asyncio from unittest.mock import AsyncMock, Mock from noxus_sdk.client import Client @pytest.mark.asyncio async def test_async_workflow_creation(): # Mock the client mock_client = Mock(spec=Client) mock_client.workflows.asave = AsyncMock() mock_client.workflows.asave.return_value = Mock(id="workflow_123") # Test async operation result = await mock_client.workflows.asave(Mock()) assert result.id == "workflow_123" mock_client.workflows.asave.assert_called_once() @pytest.mark.asyncio async def test_concurrent_operations(): mock_client = Mock(spec=Client) mock_client.workflows.alist = AsyncMock(return_value=[Mock(id="1"), Mock(id="2")]) mock_client.aget_models = AsyncMock(return_value=[{"name": "gpt-4"}]) # Test concurrent operations workflows, models = await asyncio.gather( mock_client.workflows.alist(), mock_client.aget_models() ) assert len(workflows) == 2 assert len(models) == 1 ``` ## Next Steps Learn advanced error handling patterns for async code Discover best practices for high-performance applications See async patterns in workflow management Explore all available async methods # Authentication Source: https://docs.noxus.ai/sdk/concepts/authentication Learn how to securely authenticate with the Noxus AI platform ## Overview The Noxus Client SDK uses API key-based authentication to securely connect to the Noxus AI platform. This guide covers everything you need to know about managing authentication in your applications. ## API Key Basics API keys are unique identifiers that authenticate your application with the Noxus platform. Each key is tied to a specific workspace and has defined permissions. Each API key belongs to a specific workspace Keys inherit permissions from your workspace role Keys can be revoked at any time from the dashboard Usage is tracked and can be monitored ## Getting Your API Key Log in to your Noxus account and navigate to your workspace Go to **Settings → Organization → Workspaces** Choose the workspace you want to create an API key for Click on the **API Keys** tab Click **Create API Key** and give it a descriptive name Copy the generated key immediately - it won't be shown again API keys are only displayed once during creation. Make sure to copy and store them securely immediately. ## Using API Keys ### Direct Initialization The most straightforward way to use an API key: ```python theme={null} from noxus_sdk.client import Client client = Client(api_key="noxus_1234567890abcdef...") ``` ### Environment Variables (Recommended) Store your API key in environment variables for better security: ```python Python theme={null} import os from noxus_sdk.client import Client # Read from environment variable client = Client(api_key=os.getenv("NOXUS_API_KEY")) # With fallback and validation api_key = os.getenv("NOXUS_API_KEY") if not api_key: raise ValueError("NOXUS_API_KEY environment variable is required") client = Client(api_key=api_key) ``` ```bash Shell theme={null} # Set environment variable export NOXUS_API_KEY="noxus_1234567890abcdef..." # Or add to your shell profile echo 'export NOXUS_API_KEY="noxus_1234567890abcdef..."' >> ~/.bashrc ``` ```bash .env File theme={null} # Create a .env file in your project root NOXUS_API_KEY=noxus_1234567890abcdef... NOXUS_BACKEND_URL=https://backend.noxus.ai ``` ### Using python-dotenv For local development, use python-dotenv to load environment variables: ```python theme={null} from dotenv import load_dotenv import os from noxus_sdk.client import Client # Load environment variables from .env file load_dotenv() client = Client(api_key=os.getenv("NOXUS_API_KEY")) ``` ## Security Best Practices ❌ **Don't do this:** ```python theme={null} # Bad - API key in source code client = Client(api_key="noxus_1234567890abcdef...") ``` ✅ **Do this instead:** ```python theme={null} # Good - API key from environment client = Client(api_key=os.getenv("NOXUS_API_KEY")) ``` Store API keys in environment variables or secure configuration systems: ```python theme={null} import os from noxus_sdk.client import Client def create_client(): api_key = os.getenv("NOXUS_API_KEY") if not api_key: raise ValueError("NOXUS_API_KEY environment variable is required") return Client(api_key=api_key) ``` Regularly rotate your API keys for better security: 1. Create a new API key in the dashboard 2. Update your environment variables 3. Test your application with the new key 4. Revoke the old key Use separate API keys for development, staging, and production: ```python theme={null} import os from noxus_sdk.client import Client def get_client(): env = os.getenv("ENVIRONMENT", "development") if env == "production": api_key = os.getenv("NOXUS_API_KEY_PROD") elif env == "staging": api_key = os.getenv("NOXUS_API_KEY_STAGING") else: api_key = os.getenv("NOXUS_API_KEY_DEV") return Client(api_key=api_key) ``` Regularly check your API key usage in the Noxus dashboard to detect any unusual activity. ## Configuration Management ### Using Configuration Classes Create a configuration class to manage your settings: ```python theme={null} import os from dataclasses import dataclass from typing import Optional @dataclass class NoxusConfig: api_key: str base_url: str = "https://backend.noxus.ai" timeout: int = 30 @classmethod def from_env(cls) -> "NoxusConfig": api_key = os.getenv("NOXUS_API_KEY") if not api_key: raise ValueError("NOXUS_API_KEY environment variable is required") return cls( api_key=api_key, base_url=os.getenv("NOXUS_BACKEND_URL", "https://backend.noxus.ai"), timeout=int(os.getenv("NOXUS_TIMEOUT", "30")) ) # Usage config = NoxusConfig.from_env() client = Client(api_key=config.api_key, base_url=config.base_url) ``` ### Using Pydantic Settings For more advanced configuration management: ```python theme={null} from pydantic import BaseSettings, Field from noxus_sdk.client import Client class NoxusSettings(BaseSettings): api_key: str = Field(..., env="NOXUS_API_KEY") base_url: str = Field("https://backend.noxus.ai", env="NOXUS_BACKEND_URL") timeout: int = Field(30, env="NOXUS_TIMEOUT") class Config: env_file = ".env" env_file_encoding = "utf-8" # Usage settings = NoxusSettings() client = Client(api_key=settings.api_key, base_url=settings.base_url) ``` ## Error Handling Handle authentication errors gracefully: ```python theme={null} import httpx from noxus_sdk.client import Client def create_authenticated_client(api_key: str) -> Client: try: client = Client(api_key=api_key) # Test the connection by making a simple API call client.get_models() return client except httpx.HTTPStatusError as e: if e.response.status_code == 401: raise ValueError("Invalid API key - please check your credentials") elif e.response.status_code == 403: raise ValueError("API key lacks required permissions") else: raise ValueError(f"Authentication failed: {e.response.status_code}") except httpx.RequestError as e: raise ValueError(f"Network error during authentication: {e}") # Usage with error handling try: client = create_authenticated_client(os.getenv("NOXUS_API_KEY")) print("✅ Authentication successful") except ValueError as e: print(f"❌ Authentication failed: {e}") ``` ## Testing Authentication ### Mock Authentication for Tests When writing tests, mock the authentication: ```python theme={null} import pytest from unittest.mock import Mock, patch from noxus_sdk.client import Client @patch('noxus_sdk.client.Client') def test_workflow_creation(mock_client_class): # Mock the client mock_client = Mock() mock_client_class.return_value = mock_client # Mock API responses mock_client.workflows.create.return_value = Mock(id="workflow_123") # Test your code client = Client(api_key="test_key") workflow = client.workflows.create(name="Test Workflow") assert workflow.id == "workflow_123" ``` ### Integration Tests with Test Keys For integration tests, use dedicated test API keys: ```python theme={null} import os import pytest from noxus_sdk.client import Client @pytest.fixture def test_client(): test_api_key = os.getenv("NOXUS_TEST_API_KEY") if not test_api_key: pytest.skip("NOXUS_TEST_API_KEY not set - skipping integration tests") return Client(api_key=test_api_key) def test_list_workflows(test_client): workflows = test_client.workflows.list() assert isinstance(workflows, list) ``` ## Troubleshooting **Error:** `401 Unauthorized` **Solutions:** * Verify your API key is correct * Check if the key has been revoked * Ensure you're using the right workspace key ```python theme={null} # Debug API key format api_key = os.getenv("NOXUS_API_KEY") print(f"API key starts with: {api_key[:10]}..." if api_key else "No API key found") ``` **Error:** `403 Forbidden` **Solutions:** * Check your workspace role and permissions * Contact your workspace admin * Verify you're accessing the correct workspace **Error:** Connection timeouts or network errors **Solutions:** * Check your internet connection * Verify the backend URL is correct * Check if you're behind a corporate firewall ```python theme={null} # Test with custom timeout client = Client( api_key="your_key", base_url="https://backend.noxus.ai" ) ``` ## Next Steps Learn about advanced client configuration options Understand asynchronous programming patterns Master error handling and recovery strategies Follow security and development best practices # Client Source: https://docs.noxus.ai/sdk/concepts/client Understanding the Noxus Client - your gateway to the Noxus AI platform ## Overview The `Client` class is the main entry point for all interactions with the Noxus AI platform. It handles authentication, request management, and provides access to all SDK resources including workflows, conversations, knowledge bases, and agents. ## Basic Initialization The simplest way to create a client: ```python theme={null} from noxus_sdk.client import Client client = Client(api_key="your_api_key_here") ``` ## Configuration Options The client supports several configuration options for different use cases: ```python theme={null} client = Client( api_key="your_api_key_here", base_url="https://backend.noxus.ai", # Custom backend URL load_nodes=True, # Load available node types load_me=True, # Load user information extra_headers={"Custom-Header": "value"} # Additional headers ) ``` Your Noxus API key for authentication The base URL of the Noxus backend. Can also be set via `NOXUS_BACKEND_URL` environment variable Whether to automatically load available workflow node types on initialization Whether to load user information and check admin permissions Additional HTTP headers to include with all requests ## Environment Variables The client respects these environment variables: Override the default backend URL ```bash theme={null} export NOXUS_BACKEND_URL="https://your-custom-backend.com" ``` Set your API key (though passing it directly is recommended) ```bash theme={null} export NOXUS_API_KEY="your_api_key_here" ``` ## Resource Access The client provides access to all Noxus resources through dedicated services: ```python theme={null} # Access different services workflows = client.workflows.list() conversations = client.conversations.list() knowledge_bases = client.knowledge_bases.list() agents = client.agents.list() runs = client.runs.list() # Admin functions (if you have admin permissions) if client.admin.enabled: users = client.admin.list_users() ``` `client.workflows` - Create and manage AI workflows `client.conversations` - Handle chat interactions `client.knowledge_bases` - Manage document repositories `client.agents` - Deploy autonomous AI agents `client.runs` - Monitor workflow executions `client.admin` - Administrative functions ## Platform Information Methods The client provides methods to retrieve platform capabilities: ```python theme={null} # Get available workflow node types nodes = client.get_nodes() print(f"Available node types: {len(nodes)}") # Get available AI models models = client.get_models() for model in models: print(f"Model: {model['name']} - {model['description']}") # Get chat model presets presets = client.get_chat_presets() for preset in presets: print(f"Preset: {preset['name']} - {preset['model']}") ``` ### Asynchronous Versions All platform information methods have async counterparts: ```python theme={null} import asyncio async def get_platform_info(): nodes = await client.aget_nodes() models = await client.aget_models() presets = await client.aget_chat_presets() return nodes, models, presets # Run async function nodes, models, presets = asyncio.run(get_platform_info()) ``` ## Error Handling The client handles various types of errors that can occur during API interactions: ```python theme={null} import httpx from noxus_sdk.client import Client try: client = Client(api_key="your_api_key") workflows = client.workflows.list() except httpx.HTTPStatusError as e: if e.response.status_code == 401: print("Authentication failed - check your API key") elif e.response.status_code == 403: print("Access forbidden - check your permissions") elif e.response.status_code == 429: print("Rate limit exceeded - please wait before retrying") else: print(f"HTTP error {e.response.status_code}: {e.response.text}") except httpx.RequestError as e: print(f"Network error: {e}") except Exception as e: print(f"Unexpected error: {e}") ``` ## Best Practices Never hardcode API keys in your source code: ```python theme={null} import os from noxus_sdk.client import Client # ✅ Good - use environment variables client = Client(api_key=os.getenv("NOXUS_API_KEY")) # ❌ Bad - hardcoded API key client = Client(api_key="noxus_1234567890abcdef") ``` Create one client instance and reuse it throughout your application: ```python theme={null} # ✅ Good - singleton pattern class NoxusService: _client = None @classmethod def get_client(cls): if cls._client is None: cls._client = Client(api_key=os.getenv("NOXUS_API_KEY")) return cls._client # Use throughout your app client = NoxusService.get_client() ``` Always implement proper error handling: ```python theme={null} def safe_api_call(func, *args, **kwargs): try: return func(*args, **kwargs) except httpx.HTTPStatusError as e: logger.error(f"API error {e.response.status_code}: {e.response.text}") raise except httpx.RequestError as e: logger.error(f"Network error: {e}") raise # Usage workflows = safe_api_call(client.workflows.list) ``` Be mindful of resource usage with large datasets: ```python theme={null} # ✅ Good - use pagination page = 1 while True: workflows = client.workflows.list(page=page, page_size=50) if not workflows: break process_workflows(workflows) page += 1 # ❌ Bad - loading everything at once all_workflows = client.workflows.list(page_size=10000) ``` ## Advanced Configuration ### Custom HTTP Client For advanced use cases, you can customize the underlying HTTP client: ```python theme={null} import httpx from noxus_sdk.client import Client # Create custom HTTP client with specific settings http_client = httpx.Client( timeout=60.0, limits=httpx.Limits(max_connections=100, max_keepalive_connections=20) ) # Note: This is a conceptual example - the actual SDK doesn't expose this yet # but it shows the kind of customization that might be useful ``` ### Logging Configuration Enable detailed logging for debugging: ```python theme={null} import logging from noxus_sdk.client import Client # Configure logging logging.basicConfig(level=logging.DEBUG) logger = logging.getLogger("noxus_sdk") client = Client(api_key="your_api_key") # Now all HTTP requests will be logged workflows = client.workflows.list() ``` ## Testing with the Client When writing tests, consider using dependency injection: ```python theme={null} import pytest from unittest.mock import Mock from noxus_sdk.client import Client class MyService: def __init__(self, client: Client): self.client = client def get_workflow_count(self): workflows = self.client.workflows.list() return len(workflows) # Test with mock client def test_get_workflow_count(): mock_client = Mock(spec=Client) mock_client.workflows.list.return_value = [Mock(), Mock(), Mock()] service = MyService(mock_client) count = service.get_workflow_count() assert count == 3 mock_client.workflows.list.assert_called_once() ``` ## Next Steps Learn about API key management and security Understand asynchronous programming with the SDK Start building AI workflows Explore the complete client API # Creating Conversations Source: https://docs.noxus.ai/sdk/conversations/creating-conversations Learn how to create and configure conversations with different settings and tools ## Basic Conversation Creation Creating a conversation is the first step to building any conversational AI application. Every conversation requires settings that define its behavior and capabilities. ```python theme={null} from noxus_sdk.client import Client from noxus_sdk.resources.conversations import ConversationSettings client = Client(api_key="your_api_key_here") # Basic conversation settings settings = ConversationSettings( model=["gpt-4o-mini"], temperature=0.7, max_tokens=300, extra_instructions="You are a helpful assistant." ) # Create the conversation conversation = client.conversations.create( name="My First Conversation", settings=settings ) print(f"Created conversation: {conversation.id}") ``` ## Configuration Options ### Model Selection Choose the appropriate AI model for your use case: ```python theme={null} # Single model settings = ConversationSettings( model=["gpt-4o-mini"] # Fast and cost-effective ) # Multiple models (fallback order) settings = ConversationSettings( model=["gpt-4o", "gpt-4o-mini"] # Try GPT-4o first, fallback to mini ) # Different models for different purposes creative_settings = ConversationSettings( model=["gpt-4o"], # More creative and capable temperature=0.8 ) factual_settings = ConversationSettings( model=["gpt-4o-mini"], # Fast for factual queries temperature=0.2 ) ``` ### Temperature and Creativity Control the randomness and creativity of responses: ```python theme={null} # Very deterministic (good for factual content) factual_settings = ConversationSettings( model=["gpt-4o-mini"], temperature=0.0, # Most deterministic extra_instructions="Provide factual, consistent answers." ) # Balanced (good for general conversation) balanced_settings = ConversationSettings( model=["gpt-4o-mini"], temperature=0.7, # Balanced creativity extra_instructions="Be helpful and engaging." ) # Creative (good for content generation) creative_settings = ConversationSettings( model=["gpt-4o"], temperature=0.9, # Most creative extra_instructions="Be creative and think outside the box." ) ``` ### Token Limits Control response length and cost: ```python theme={null} # Short responses concise_settings = ConversationSettings( model=["gpt-4o-mini"], max_tokens=100, # Brief responses extra_instructions="Keep responses under 50 words." ) # Medium responses standard_settings = ConversationSettings( model=["gpt-4o-mini"], max_tokens=500, # Standard length extra_instructions="Provide comprehensive but concise answers." ) # Long responses detailed_settings = ConversationSettings( model=["gpt-4o"], max_tokens=1500, # Detailed responses extra_instructions="Provide thorough, detailed explanations." ) ``` ## Adding Tools Tools extend conversation capabilities beyond basic text generation: ### Web Research Tool ```python theme={null} from noxus_sdk.resources.conversations import WebResearchTool web_tool = WebResearchTool( enabled=True, extra_instructions="Focus on recent, reliable sources. Always cite sources." ) research_settings = ConversationSettings( model=["gpt-4o-mini"], temperature=0.4, max_tokens=600, tools=[web_tool], extra_instructions="Use web research when you need current information." ) research_bot = client.conversations.create( name="Research Assistant", settings=research_settings ) ``` ### Knowledge Base Integration ```python theme={null} from noxus_sdk.resources.conversations import KnowledgeBaseQaTool # Single knowledge base kb_tool = KnowledgeBaseQaTool( enabled=True, kb_id="your_knowledge_base_id", extra_instructions="Provide detailed answers with specific references." ) # Multiple knowledge bases with selector from noxus_sdk.resources.conversations import KnowledgeBaseSelectorTool kb_selector = KnowledgeBaseSelectorTool( enabled=True, extra_instructions="Choose the most relevant knowledge base for each query." ) kb_settings = ConversationSettings( model=["gpt-4o-mini"], temperature=0.3, tools=[kb_tool, kb_selector], extra_instructions="Always check knowledge bases first for company-specific information." ) support_bot = client.conversations.create( name="Support Bot", settings=kb_settings ) ``` ### Workflow Integration ```python theme={null} from noxus_sdk.resources.conversations import WorkflowTool workflow_tool = WorkflowTool( enabled=True, workflow={ "id": "data_analysis_workflow_id", "name": "Data Analyzer", "description": "Analyze datasets and generate insights" }, name="Data Analysis", description="Run data analysis on uploaded files" ) analyst_settings = ConversationSettings( model=["gpt-4o"], temperature=0.4, tools=[workflow_tool], extra_instructions="Use the data analysis workflow for any data-related requests." ) data_analyst = client.conversations.create( name="Data Analyst Bot", settings=analyst_settings ) ``` ## Specialized Conversation Types ### Customer Support Bot ```python theme={null} from noxus_sdk.resources.conversations import ( ConversationSettings, KnowledgeBaseQaTool, WebResearchTool ) # Support bot with knowledge base and escalation support_kb_tool = KnowledgeBaseQaTool( enabled=True, kb_id="support_kb_id", extra_instructions="Check for existing solutions and troubleshooting steps." ) support_settings = ConversationSettings( model=["gpt-4o-mini"], temperature=0.3, # Consistent, helpful responses max_tokens=400, tools=[support_kb_tool], extra_instructions="""You are a customer support assistant. - Always be empathetic and helpful - Check the knowledge base for solutions first - If you can't solve the issue, suggest contacting human support - Ask clarifying questions when needed""" ) support_bot = client.conversations.create( name="Customer Support Bot", settings=support_settings ) ``` ### Content Creation Assistant ```python theme={null} content_settings = ConversationSettings( model=["gpt-4o"], temperature=0.7, # Creative but controlled max_tokens=800, tools=[WebResearchTool( enabled=True, extra_instructions="Research current trends and examples" )], extra_instructions="""You are a content creation assistant. - Help with blog posts, social media, and marketing copy - Research current trends when relevant - Ask about target audience and goals - Provide multiple options when possible""" ) content_assistant = client.conversations.create( name="Content Creator", settings=content_settings ) ``` ### Technical Documentation Bot ```python theme={null} from noxus_sdk.resources.conversations import NoxusQaTool tech_doc_settings = ConversationSettings( model=["gpt-4o"], temperature=0.2, # Precise, technical responses max_tokens=1000, tools=[ KnowledgeBaseQaTool( enabled=True, kb_id="technical_docs_kb_id", extra_instructions="Reference official documentation and code examples" ), NoxusQaTool( enabled=True, extra_instructions="Help with Noxus platform questions" ) ], extra_instructions="""You are a technical documentation assistant. - Provide accurate, detailed technical information - Include code examples when relevant - Reference official documentation - Ask for clarification on technical requirements""" ) tech_bot = client.conversations.create( name="Technical Assistant", settings=tech_doc_settings ) ``` ## Advanced Configuration ### Multi-Tool Conversations ```python theme={null} from noxus_sdk.resources.conversations import ( WebResearchTool, KnowledgeBaseQaTool, WorkflowTool, NoxusQaTool ) # Comprehensive assistant with multiple capabilities multi_tool_settings = ConversationSettings( model=["gpt-4o"], temperature=0.6, max_tokens=600, tools=[ WebResearchTool( enabled=True, extra_instructions="Use for current events and recent information" ), KnowledgeBaseQaTool( enabled=True, kb_id="company_kb_id", extra_instructions="Use for company policies and procedures" ), WorkflowTool( enabled=True, workflow={ "id": "report_generator_id", "name": "Report Generator", "description": "Generate formatted reports" } ), NoxusQaTool( enabled=True, extra_instructions="Help with Noxus platform questions" ) ], extra_instructions="""You are a versatile AI assistant with multiple tools: - Use web research for current information - Check company knowledge base for internal information - Use workflows for complex data processing - Help with Noxus platform questions - Choose the most appropriate tool for each request""" ) versatile_assistant = client.conversations.create( name="Versatile Assistant", settings=multi_tool_settings ) ``` ### Conversation with Agent Create conversations that use pre-configured agents: ```python theme={null} # First, get an existing agent agent = client.agents.get("agent_id_here") # Create conversation using the agent's settings agent_conversation = client.conversations.create( name="Chat with Agent", agent_id=agent.id # Uses agent's settings automatically ) # No need to specify settings - they come from the agent print(f"Created conversation with agent: {agent.name}") ``` ## Asynchronous Creation For high-performance applications: ```python theme={null} import asyncio async def create_multiple_conversations(): client = Client(api_key="your_api_key_here") # Define different conversation types conversation_configs = [ { "name": "Support Bot", "settings": ConversationSettings( model=["gpt-4o-mini"], temperature=0.3, tools=[KnowledgeBaseQaTool(enabled=True, kb_id="support_kb")] ) }, { "name": "Content Creator", "settings": ConversationSettings( model=["gpt-4o"], temperature=0.7, tools=[WebResearchTool(enabled=True)] ) }, { "name": "Data Analyst", "settings": ConversationSettings( model=["gpt-4o"], temperature=0.4, tools=[WorkflowTool( enabled=True, workflow={"id": "analysis_workflow", "name": "Analyzer"} )] ) } ] # Create all conversations concurrently tasks = [ client.conversations.acreate( name=config["name"], settings=config["settings"] ) for config in conversation_configs ] conversations = await asyncio.gather(*tasks) return conversations # Create conversations asynchronously conversations = asyncio.run(create_multiple_conversations()) for conv in conversations: print(f"Created: {conv.name} (ID: {conv.id})") ``` ## Validation and Testing ### Validate Settings ```python theme={null} def validate_conversation_settings(settings): """Validate conversation settings before creation""" # Check required fields if not settings.model: raise ValueError("Model is required") # Validate temperature range if not 0.0 <= settings.temperature <= 1.0: raise ValueError("Temperature must be between 0.0 and 1.0") # Validate max_tokens if settings.max_tokens <= 0: raise ValueError("max_tokens must be positive") # Check tool configuration for tool in settings.tools: if hasattr(tool, 'kb_id') and not tool.kb_id: raise ValueError("Knowledge base tool requires kb_id") return True # Use validation try: validate_conversation_settings(settings) conversation = client.conversations.create(name="Test", settings=settings) except ValueError as e: print(f"Invalid settings: {e}") ``` ### Test Conversation ```python theme={null} def test_conversation(conversation, test_messages): """Test a conversation with sample messages""" results = [] for message_text in test_messages: try: message = MessageRequest(content=message_text) response = conversation.add_message(message) results.append({ "input": message_text, "output": response.message_parts, "success": True }) except Exception as e: results.append({ "input": message_text, "output": str(e), "success": False }) return results # Test the conversation test_messages = [ "Hello, how can you help me?", "What's the weather like today?", "Can you help me write a blog post?" ] test_results = test_conversation(support_bot, test_messages) for result in test_results: status = "✅" if result["success"] else "❌" print(f"{status} {result['input'][:30]}... -> {result['output'][:50]}...") ``` ## Best Practices Use descriptive names for conversations: ```python theme={null} # ✅ Good - descriptive and purposeful conversation = client.conversations.create( name="Customer Support - Product Questions", settings=settings ) # ❌ Bad - generic and unclear conversation = client.conversations.create( name="Bot 1", settings=settings ) ``` Create reusable settings configurations: ```python theme={null} # Define common settings SUPPORT_SETTINGS = ConversationSettings( model=["gpt-4o-mini"], temperature=0.3, max_tokens=400, extra_instructions="Be helpful and empathetic" ) CREATIVE_SETTINGS = ConversationSettings( model=["gpt-4o"], temperature=0.8, max_tokens=800, extra_instructions="Be creative and engaging" ) # Use predefined settings support_bot = client.conversations.create( name="Support Bot", settings=SUPPORT_SETTINGS ) ``` Only enable tools that are needed: ```python theme={null} # ✅ Good - specific tools for specific purposes support_tools = [ KnowledgeBaseQaTool(enabled=True, kb_id="support_kb") ] research_tools = [ WebResearchTool(enabled=True), KnowledgeBaseQaTool(enabled=True, kb_id="research_kb") ] # ❌ Bad - too many tools without clear purpose all_tools = [web_tool, kb_tool1, kb_tool2, workflow_tool, noxus_tool] ``` ## Next Steps Learn how to send messages and handle responses Explore all available conversation tools in detail See complete conversation implementations Detailed API reference for conversation settings # Conversations Overview Source: https://docs.noxus.ai/sdk/conversations/overview Build intelligent conversational AI applications with the Noxus Client SDK ## What are Conversations? Conversations in Noxus represent interactive chat sessions with AI models. They provide a structured way to build chatbots, virtual assistants, and other conversational AI applications with support for multiple AI models, custom tools, context management, and file handling. Conversation Flow Conversation Flow ## Key Features Choose from various AI models including GPT-4, Claude, and more Enhance conversations with web search, knowledge bases, and custom tools Maintain conversation history and context across multiple interactions Process and discuss documents, images, and other file types Build responsive applications with asynchronous operations Fine-tune model parameters, temperature, tokens, and behavior ## Core Concepts ### Conversation Settings Every conversation is configured with settings that control its behavior: ```python theme={null} from noxus_sdk.resources.conversations import ConversationSettings settings = ConversationSettings( model=["gpt-4o-mini"], # AI model(s) to use temperature=0.7, # Creativity level (0.0-1.0) max_tokens=500, # Maximum response length tools=[], # Available tools extra_instructions="Be helpful" # Additional instructions ) ``` ### Message Flow Conversations follow a simple request-response pattern: 1. **Create** a conversation with specific settings 2. **Send** messages with text, files, or tool requests 3. **Receive** AI responses with generated content 4. **Continue** the conversation with follow-up messages ```mermaid theme={null} sequenceDiagram participant User participant SDK participant Noxus API participant AI Model User->>SDK: Create conversation SDK->>Noxus API: POST /conversations Noxus API-->>SDK: Conversation created User->>SDK: Send message SDK->>Noxus API: POST /messages Noxus API->>AI Model: Process message AI Model-->>Noxus API: Generate response Noxus API-->>SDK: Message response SDK-->>User: AI response ``` ## Basic Usage ### Creating a Simple Conversation ```python theme={null} from noxus_sdk.client import Client from noxus_sdk.resources.conversations import ConversationSettings, MessageRequest # Initialize client client = Client(api_key="your_api_key_here") # Configure conversation settings settings = ConversationSettings( model=["gpt-4o-mini"], temperature=0.7, max_tokens=150, extra_instructions="You are a helpful assistant. Be concise and friendly." ) # Create conversation conversation = client.conversations.create( name="My Chat Assistant", settings=settings ) print(f"Created conversation: {conversation.id}") ``` ### Sending Messages ```python theme={null} # Send a simple text message message = MessageRequest(content="Hello! How can you help me today?") response = conversation.add_message(message) print(f"AI Response: {response.message_parts}") # Continue the conversation follow_up = MessageRequest(content="Can you explain quantum computing in simple terms?") response = conversation.add_message(follow_up) print(f"AI Response: {response.message_parts}") ``` ### Working with Files ```python theme={null} import base64 # Prepare file for upload with open("document.pdf", "rb") as file: file_content = base64.b64encode(file.read()).decode("utf-8") from noxus_sdk.resources.conversations import ConversationFile # Create file object conversation_file = ConversationFile( name="document.pdf", status="success", b64_content=file_content ) # Send message with file message = MessageRequest( content="Please summarize this document", files=[conversation_file] ) response = conversation.add_message(message) print(f"Document Summary: {response.message_parts}") ``` ## Conversation Tools Tools extend conversation capabilities beyond basic text generation: ### Available Tools Enable AI to search the web for current information: ```python theme={null} from noxus_sdk.resources.conversations import WebResearchTool web_tool = WebResearchTool( enabled=True, extra_instructions="Focus on recent and reliable sources" ) ``` Access your knowledge bases for specialized information: ```python theme={null} from noxus_sdk.resources.conversations import KnowledgeBaseQaTool kb_tool = KnowledgeBaseQaTool( enabled=True, kb_id="your_knowledge_base_id", extra_instructions="Provide detailed answers with citations" ) ``` Execute workflows from within conversations: ```python theme={null} from noxus_sdk.resources.conversations import WorkflowTool workflow_tool = WorkflowTool( enabled=True, workflow={ "id": "workflow_id", "name": "Data Processor", "description": "Process and analyze data" } ) ``` Get help with Noxus platform features: ```python theme={null} from noxus_sdk.resources.conversations import NoxusQaTool noxus_tool = NoxusQaTool( enabled=True, extra_instructions="Explain features clearly with examples" ) ``` ### Using Tools in Conversations ```python theme={null} from noxus_sdk.resources.conversations import ( ConversationSettings, WebResearchTool, KnowledgeBaseQaTool ) # Configure tools web_research = WebResearchTool( enabled=True, extra_instructions="Focus on recent developments and reliable sources" ) kb_tool = KnowledgeBaseQaTool( enabled=True, kb_id="company_kb_id", extra_instructions="Reference company policies and procedures" ) # Create conversation with tools settings = ConversationSettings( model=["gpt-4o-mini"], temperature=0.7, max_tokens=300, tools=[web_research, kb_tool], extra_instructions="Use tools when needed to provide accurate, up-to-date information" ) conversation = client.conversations.create( name="Research Assistant", settings=settings ) # Ask questions that trigger tool usage message = MessageRequest( content="What are the latest developments in renewable energy technology?", tool="web_research" # Explicitly request web research ) response = conversation.add_message(message) print(f"Research Results: {response.message_parts}") ``` ## Advanced Features ### Asynchronous Operations For high-performance applications: ```python theme={null} import asyncio async def async_conversation_example(): client = Client(api_key="your_api_key_here") # Create conversation asynchronously settings = ConversationSettings( model=["gpt-4o-mini"], temperature=0.7, max_tokens=200 ) conversation = await client.conversations.acreate( name="Async Chat", settings=settings ) # Send message asynchronously message = MessageRequest(content="Explain machine learning briefly") response = await conversation.aadd_message(message) return response.message_parts # Run async conversation result = asyncio.run(async_conversation_example()) print(result) ``` ### Conversation Management ```python theme={null} # List all conversations conversations = client.conversations.list(page=1, page_size=10) for conv in conversations: print(f"Conversation: {conv.name} (ID: {conv.id})") # Get specific conversation conversation = client.conversations.get("conversation_id") # Get conversation messages messages = conversation.get_messages() for msg in messages: print(f"Message: {msg.content[:50]}...") # Update conversation settings new_settings = ConversationSettings( model=["gpt-4o"], # Upgrade to more powerful model temperature=0.5, max_tokens=400 ) updated_conversation = client.conversations.update( conversation_id=conversation.id, name="Updated Chat", settings=new_settings ) # Delete conversation client.conversations.delete(conversation_id=conversation.id) ``` ## Use Cases Build intelligent support bots that can access knowledge bases and escalate to humans Create writing assistants that help with blogs, marketing copy, and documentation Build tools that can search the web and analyze documents for insights Create personalized learning experiences with adaptive questioning Build programming helpers that can explain code and suggest improvements Create assistants that can interpret data and generate reports ## Conversation Patterns ### Question-Answer Bot ```python theme={null} # Simple Q&A bot with knowledge base qa_settings = ConversationSettings( model=["gpt-4o-mini"], temperature=0.3, # Lower temperature for factual responses tools=[KnowledgeBaseQaTool( enabled=True, kb_id="faq_kb_id", extra_instructions="Provide accurate answers based on the knowledge base" )], extra_instructions="You are a helpful FAQ bot. Always check the knowledge base first." ) qa_bot = client.conversations.create(name="FAQ Bot", settings=qa_settings) ``` ### Research Assistant ```python theme={null} # Research assistant with web access research_settings = ConversationSettings( model=["gpt-4o"], temperature=0.4, tools=[ WebResearchTool( enabled=True, extra_instructions="Use recent, authoritative sources" ) ], extra_instructions="You are a research assistant. Always cite your sources and provide comprehensive answers." ) research_assistant = client.conversations.create( name="Research Assistant", settings=research_settings ) ``` ### Multi-Tool Assistant ```python theme={null} # Comprehensive assistant with multiple tools multi_tool_settings = ConversationSettings( model=["gpt-4o"], temperature=0.6, tools=[ WebResearchTool(enabled=True), KnowledgeBaseQaTool( enabled=True, kb_id="company_kb_id" ), WorkflowTool( enabled=True, workflow={ "id": "data_analysis_workflow_id", "name": "Data Analyzer", "description": "Analyze datasets and generate insights" } ) ], extra_instructions="You are a versatile assistant. Use the most appropriate tool for each request." ) multi_assistant = client.conversations.create( name="Multi-Tool Assistant", settings=multi_tool_settings ) ``` ## Best Practices Choose the right model for your use case: * **gpt-4o-mini**: Fast, cost-effective for simple tasks * **gpt-4o**: More capable for complex reasoning * **claude-3-sonnet**: Good balance of speed and capability ```python theme={null} # For simple Q&A settings = ConversationSettings(model=["gpt-4o-mini"]) # For complex analysis settings = ConversationSettings(model=["gpt-4o"]) ``` Adjust creativity based on your needs: ```python theme={null} # Factual, consistent responses settings = ConversationSettings(temperature=0.1) # Balanced responses settings = ConversationSettings(temperature=0.7) # Creative responses settings = ConversationSettings(temperature=0.9) ``` Manage conversation length and context: ```python theme={null} # Limit response length settings = ConversationSettings(max_tokens=300) # Provide clear instructions settings = ConversationSettings( extra_instructions="Keep responses under 100 words. Be direct and helpful." ) ``` Implement robust error handling: ```python theme={null} try: response = conversation.add_message(message) return response.message_parts except Exception as e: print(f"Error in conversation: {e}") return "I'm sorry, I encountered an error. Please try again." ``` ## Performance Considerations * Use faster models for simple tasks * Set appropriate max\_tokens limits * Consider async operations for multiple conversations * Choose cost-effective models when possible * Limit token usage with max\_tokens * Use tools strategically to reduce model calls * Monitor conversation length * Implement context pruning for long conversations * Use summarization for context compression * Enable only necessary tools * Provide clear tool instructions * Monitor tool usage and costs ## Next Steps Learn how to create and configure conversations Master message handling and conversation flow Explore all available conversation tools See real-world conversation implementations # Troubleshooting Source: https://docs.noxus.ai/sdk/guides/troubleshooting Common issues and solutions when using the Noxus Client SDK ## Common Issues ### Authentication Problems **Problem**: Getting `401 Unauthorized` when making API calls. **Causes**: * Invalid API key * Expired API key * API key not properly set **Solutions**: ```python theme={null} # Check if API key is set import os api_key = os.getenv("NOXUS_API_KEY") if not api_key: print("API key not found in environment variables") # Verify API key format if api_key and not api_key.startswith("noxus_"): print("API key format appears incorrect") # Test authentication try: client = Client(api_key=api_key) models = client.get_models() # Simple test call print("✅ Authentication successful") except httpx.HTTPStatusError as e: if e.response.status_code == 401: print("❌ Invalid API key") ``` **Problem**: Getting `403 Forbidden` when accessing certain resources. **Causes**: * Insufficient permissions for your workspace role * Trying to access resources from another workspace * API key doesn't have required permissions **Solutions**: * Contact your workspace administrator * Verify you're using the correct workspace API key * Check your role permissions in the Noxus dashboard ```python theme={null} # Check your user permissions try: client = Client(api_key="your_key") if client.admin.enabled: print("✅ You have admin permissions") else: print("ℹ️ Limited permissions - some features may not be available") except Exception as e: print(f"❌ Error checking permissions: {e}") ``` ### Connection Issues **Problem**: Requests timing out or taking too long. **Solutions**: ```python theme={null} import httpx from noxus_sdk.client import Client # For long-running operations, increase timeout try: client = Client(api_key="your_key") # Use async for better timeout handling import asyncio async def with_timeout(): try: result = await asyncio.wait_for( client.workflows.alist(), timeout=60.0 # 60 second timeout ) return result except asyncio.TimeoutError: print("Operation timed out") return None workflows = asyncio.run(with_timeout()) except httpx.RequestError as e: print(f"Network error: {e}") ``` **Problem**: Cannot connect to the Noxus backend. **Causes**: * Network connectivity issues * Firewall blocking requests * Incorrect backend URL **Solutions**: ```python theme={null} import requests # Test basic connectivity def test_connectivity(): try: response = requests.get("https://backend.noxus.ai/health", timeout=10) if response.status_code == 200: print("✅ Backend is reachable") else: print(f"⚠️ Backend returned status: {response.status_code}") except requests.exceptions.ConnectionError: print("❌ Cannot connect to backend") except requests.exceptions.Timeout: print("❌ Connection timed out") test_connectivity() # Check if using custom backend URL backend_url = os.getenv("NOXUS_BACKEND_URL") if backend_url: print(f"Using custom backend: {backend_url}") ``` ### Workflow Issues **Problem**: Cannot create or save workflows. **Common Issues**: ```python theme={null} from noxus_sdk.workflows import WorkflowDefinition # Issue 1: Missing required node configurations def check_node_config(workflow_def): for node in workflow_def.nodes: if node.type == "TextGenerationNode": if not hasattr(node.config, 'template') or not node.config.template: print(f"❌ Node '{node.label}' missing template") if not hasattr(node.config, 'model') or not node.config.model: print(f"❌ Node '{node.label}' missing model") # Issue 2: Invalid connections def check_connections(workflow_def): node_ids = {node.id for node in workflow_def.nodes} for edge in workflow_def.edges: if edge.source_node_id not in node_ids: print(f"❌ Invalid source node: {edge.source_node_id}") if edge.target_node_id not in node_ids: print(f"❌ Invalid target node: {edge.target_node_id}") # Issue 3: Orphaned nodes def check_orphaned_nodes(workflow_def): connected_nodes = set() for edge in workflow_def.edges: connected_nodes.add(edge.source_node_id) connected_nodes.add(edge.target_node_id) orphaned = [node for node in workflow_def.nodes if node.id not in connected_nodes] if orphaned: print(f"⚠️ Orphaned nodes: {[n.label for n in orphaned]}") ``` **Problem**: Workflows fail during execution. **Debugging Steps**: ```python theme={null} def debug_workflow_execution(workflow, input_data): try: # Start workflow run = workflow.run(body=input_data) print(f"Started run: {run.id}") # Monitor execution while run.status not in ["completed", "failed", "cancelled"]: print(f"Status: {run.status}") time.sleep(2) run = run.refresh() if run.status == "completed": print(f"✅ Success: {run.output}") else: print(f"❌ Failed: {run.error_message}") if hasattr(run, 'error_details'): print(f"Details: {run.error_details}") except Exception as e: print(f"❌ Execution error: {e}") # Test with minimal input debug_workflow_execution(workflow, {"test_input": "hello world"}) ``` ### Conversation Issues **Problem**: Cannot send messages or get responses. **Solutions**: ```python theme={null} from noxus_sdk.resources.conversations import MessageRequest def debug_conversation(conversation): try: # Test basic message test_message = MessageRequest(content="Hello, can you hear me?") response = conversation.add_message(test_message) if response and response.message_parts: print("✅ Conversation working") print(f"Response: {response.message_parts}") else: print("❌ Empty response received") except Exception as e: print(f"❌ Message error: {e}") # Check conversation settings print(f"Model: {conversation.settings.model}") print(f"Max tokens: {conversation.settings.max_tokens}") print(f"Tools: {len(conversation.settings.tools)}") debug_conversation(conversation) ``` **Problem**: Conversation tools not working as expected. **Debugging**: ```python theme={null} def debug_tools(conversation): print("Enabled tools:") for tool in conversation.settings.tools: print(f"- {tool.__class__.__name__}: {tool.enabled}") # Check tool-specific configuration if hasattr(tool, 'kb_id'): print(f" KB ID: {tool.kb_id}") if hasattr(tool, 'workflow'): print(f" Workflow: {tool.workflow}") # Test tool usage explicitly def test_tool_usage(conversation, tool_name): try: message = MessageRequest( content="Test message for tool", tool=tool_name ) response = conversation.add_message(message) print(f"✅ Tool '{tool_name}' working") except Exception as e: print(f"❌ Tool '{tool_name}' error: {e}") debug_tools(conversation) test_tool_usage(conversation, "web_research") ``` ### Knowledge Base Issues **Problem**: Cannot upload documents to knowledge base. **Solutions**: ```python theme={null} def debug_kb_upload(kb, file_path): import os # Check file exists and size if not os.path.exists(file_path): print(f"❌ File not found: {file_path}") return file_size = os.path.getsize(file_path) / (1024 * 1024) # MB print(f"File size: {file_size:.2f} MB") if file_size > 50: # Assuming 50MB limit print("⚠️ File may be too large") # Check file type file_ext = os.path.splitext(file_path)[1].lower() supported_types = kb.document_types or [] if file_ext.lstrip('.') not in supported_types: print(f"❌ Unsupported file type: {file_ext}") print(f"Supported types: {supported_types}") return try: run_ids = kb.upload_document(files=[file_path]) print(f"✅ Upload started: {run_ids}") # Monitor upload progress import time for _ in range(30): # Wait up to 5 minutes kb.refresh() print(f"KB status: {kb.status}") if kb.status == "ready": break time.sleep(10) except Exception as e: print(f"❌ Upload error: {e}") debug_kb_upload(kb, "document.pdf") ``` **Problem**: Knowledge base stuck in processing state. **Solutions**: ```python theme={null} def check_kb_status(kb): kb.refresh() print(f"Status: {kb.status}") print(f"Total documents: {kb.total_documents}") print(f"Trained documents: {kb.trained_documents}") print(f"Error documents: {kb.error_documents}") if kb.error_documents > 0: print("⚠️ Some documents failed to process") # Check individual document status documents = kb.list_documents() for doc in documents: if doc.status == "error": print(f"❌ Failed document: {doc.name}") if kb.status == "processing": print("ℹ️ Knowledge base still processing...") # Check processing runs runs = kb.get_runs(status="running") print(f"Active runs: {len(runs)}") check_kb_status(kb) ``` ## Performance Issues ### Slow Response Times ```python theme={null} import time from contextlib import contextmanager @contextmanager def timer(): start = time.time() yield end = time.time() print(f"Operation took {end - start:.2f} seconds") # Measure operation times with timer(): workflows = client.workflows.list() # Use async for better performance import asyncio async def fast_operations(): # Run multiple operations concurrently tasks = [ client.workflows.alist(), client.conversations.alist(), client.knowledge_bases.alist() ] results = await asyncio.gather(*tasks) return results with timer(): results = asyncio.run(fast_operations()) ``` ### Memory Usage ```python theme={null} import psutil import os def check_memory_usage(): process = psutil.Process(os.getpid()) memory_mb = process.memory_info().rss / 1024 / 1024 print(f"Memory usage: {memory_mb:.2f} MB") # Monitor memory during operations check_memory_usage() # Process large datasets in batches def process_large_dataset(items, batch_size=10): for i in range(0, len(items), batch_size): batch = items[i:i + batch_size] # Process batch yield batch check_memory_usage() # Use generators for large results def get_all_workflows_generator(client): page = 1 while True: workflows = client.workflows.list(page=page, page_size=50) if not workflows: break for workflow in workflows: yield workflow page += 1 ``` ## Debugging Tools ### Enable Debug Logging ```python theme={null} import logging # Configure detailed logging logging.basicConfig( level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) # Enable HTTP request logging import httpx httpx_logger = logging.getLogger("httpx") httpx_logger.setLevel(logging.DEBUG) # Now all HTTP requests will be logged client = Client(api_key="your_key") workflows = client.workflows.list() ``` ### Request/Response Inspection ```python theme={null} import httpx class DebugClient(Client): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) def request(self, method, url, **kwargs): print(f"🔄 {method} {url}") if 'json' in kwargs: print(f"📤 Request body: {kwargs['json']}") try: response = super().request(method, url, **kwargs) print(f"✅ Response: {response.status_code}") return response except Exception as e: print(f"❌ Error: {e}") raise # Use debug client debug_client = DebugClient(api_key="your_key") workflows = debug_client.workflows.list() ``` ## Getting Help ### Collect Diagnostic Information ```python theme={null} def collect_diagnostics(): import sys import platform print("=== Noxus SDK Diagnostics ===") print(f"Python version: {sys.version}") print(f"Platform: {platform.platform()}") try: import noxus_sdk print(f"Noxus SDK version: {noxus_sdk.__version__}") except: print("Noxus SDK version: unknown") # Test basic connectivity try: client = Client(api_key=os.getenv("NOXUS_API_KEY")) models = client.get_models() print(f"✅ API connection working ({len(models)} models available)") except Exception as e: print(f"❌ API connection failed: {e}") # Check environment print(f"API key set: {'Yes' if os.getenv('NOXUS_API_KEY') else 'No'}") print(f"Backend URL: {os.getenv('NOXUS_BACKEND_URL', 'default')}") collect_diagnostics() ``` ### Contact Support When contacting support, include: 1. **Error message** - Full error text and stack trace 2. **Code snippet** - Minimal code that reproduces the issue 3. **Environment** - Python version, OS, SDK version 4. **Expected behavior** - What you expected to happen 5. **Actual behavior** - What actually happened ```python theme={null} # Template for bug reports bug_report_template = """ ## Bug Report **Error Message:** ``` \[Paste full error message here] ```` **Code to Reproduce:** ```python [Minimal code that reproduces the issue] ```` **Environment:** * Python version: * OS: * Noxus SDK version: * Backend URL: **Expected Behavior:** \[What you expected to happen] **Actual Behavior:** \[What actually happened] **Additional Context:** \[Any other relevant information] """ print(bug\_report\_template) ``` ## Next Steps Learn best practices for using the SDK effectively Detailed API documentation for all SDK features Working examples for common use cases Get help from the Noxus community ``` # Installation Source: https://docs.noxus.ai/sdk/installation Learn how to install and set up the Noxus Client SDK ## System Requirements Python 3.10 or later Windows, macOS, Linux ## Installation Methods ### Using pip (Recommended) The easiest way to install the Noxus Client SDK is using pip: ```bash theme={null} pip install noxus-sdk ``` ### Using Poetry If you're using Poetry for dependency management: ```bash theme={null} poetry add noxus-sdk ``` ### Using Pipenv For Pipenv users: ```bash theme={null} pipenv install noxus-sdk ``` ### Development Installation If you want to install from source or contribute to the SDK: ```bash theme={null} git clone https://github.com/noxus-ai/noxus-client-sdk.git cd noxus-client-sdk pip install -e . ``` The `-e` flag installs the package in "editable" mode, which is useful for development. ## Dependencies The SDK automatically installs the following dependencies: * **pydantic** (≥2.0) - Data validation and serialization * **httpx** - HTTP client for API requests * **anyio** - Asynchronous I/O support * **aiofiles** - Asynchronous file operations For development and testing: * **pytest** (≥8.3.3) - Testing framework * **pytest-asyncio** (0.24.0) - Async testing support * **pytest-cov** (≥6.0.0) - Coverage reporting * **mypy** (1.11.2) - Type checking * **ruff** (0.6.3) - Linting and formatting ## Virtual Environment Setup We recommend using a virtual environment to avoid conflicts with other packages: ```bash venv theme={null} # Create virtual environment python -m venv noxus-env # Activate (Linux/macOS) source noxus-env/bin/activate # Activate (Windows) noxus-env\Scripts\activate # Install SDK pip install noxus-sdk ``` ```bash conda theme={null} # Create conda environment conda create -n noxus-env python=3.10 # Activate environment conda activate noxus-env # Install SDK pip install noxus-sdk ``` ```bash poetry theme={null} # Initialize new project with Poetry poetry init # Add SDK dependency poetry add noxus-sdk # Install dependencies poetry install # Activate shell poetry shell ``` ## Verify Installation Test your installation by running this simple script: ```python theme={null} from noxus_sdk.client import Client import noxus_sdk # Check version print(f"Noxus SDK version: {noxus_sdk.__version__}") # Test client initialization (without API key) try: # This will fail without API key, but confirms imports work client = Client(api_key="test") print("✅ SDK imported successfully") except Exception as e: if "api_key" in str(e).lower(): print("✅ SDK imported successfully (API key needed for actual use)") else: print(f"❌ Installation issue: {e}") ``` ## Configuration ### Environment Variables Set up your environment for easier development: ```bash .env theme={null} # Create a .env file in your project root NOXUS_API_KEY=your_api_key_here NOXUS_BACKEND_URL=https://backend.noxus.ai ``` ```python python-dotenv theme={null} # Install python-dotenv for .env file support pip install python-dotenv # Use in your code from dotenv import load_dotenv import os load_dotenv() from noxus_sdk.client import Client client = Client(api_key=os.getenv("NOXUS_API_KEY")) ``` ```bash shell theme={null} # Set environment variables in your shell export NOXUS_API_KEY="your_api_key_here" export NOXUS_BACKEND_URL="https://backend.noxus.ai" ``` ### Configuration File Create a configuration file for your project: ```python config.py theme={null} import os from dataclasses import dataclass @dataclass class NoxusConfig: api_key: str base_url: str = "https://backend.noxus.ai" timeout: int = 30 retries: int = 3 def get_config() -> NoxusConfig: return NoxusConfig( api_key=os.getenv("NOXUS_API_KEY", ""), base_url=os.getenv("NOXUS_BACKEND_URL", "https://backend.noxus.ai"), timeout=int(os.getenv("NOXUS_TIMEOUT", "30")), retries=int(os.getenv("NOXUS_RETRIES", "3")) ) ``` ## Troubleshooting Installation If you encounter permission errors during installation: ```bash theme={null} # Use --user flag to install for current user only pip install --user noxus-sdk # Or use sudo (not recommended) sudo pip install noxus-sdk ``` Ensure you're using Python 3.10 or later: `bash # Check Python version python --version # Use specific Python version if needed python3.10 -m pip install noxus-sdk ` If you're behind a corporate firewall: `bash # Use proxy settings pip install --proxy http://proxy.company.com:port noxus-sdk # Or configure pip permanently pip config set global.proxy http://proxy.company.com:port ` If you have dependency conflicts: ```bash theme={null} # Create fresh virtual environment python -m venv fresh-env source fresh-env/bin/activate # Linux/macOS # fresh-env\Scripts\activate # Windows # Install SDK in clean environment pip install noxus-sdk ``` ## IDE Setup ### VS Code For the best development experience with VS Code: 1. Install the Python extension 2. Set up your Python interpreter to use your virtual environment 3. Install these recommended extensions: * Python * Pylance * Python Docstring Generator ```json settings.json theme={null} { "python.defaultInterpreterPath": "./noxus-env/bin/python", "python.linting.enabled": true, "python.linting.pylintEnabled": false, "python.linting.flake8Enabled": true, "python.formatting.provider": "black" } ``` ### PyCharm For PyCharm users: 1. Create a new project or open existing one 2. Configure Python interpreter to use your virtual environment 3. Enable type checking and code inspection 4. Install the requirements.txt if using development installation ## Next Steps Get up and running with your first Noxus application Learn how to configure API keys and authentication Understand client initialization and configuration options Explore the complete API documentation ## Getting Help If you encounter any issues during installation, please check our [troubleshooting guide](/sdk/guides/troubleshooting) or contact [support@noxus.ai](mailto:support@noxus.ai). # Introduction Source: https://docs.noxus.ai/sdk/introduction Welcome to the Noxus Client SDK - your gateway to building powerful AI applications ## What is the Noxus Client SDK? The **Noxus Client SDK** is a comprehensive Python library designed to interact seamlessly with the Noxus AI backend. It provides a convenient, high-level interface for managing workflows, conversations, knowledge bases, agents, and more. Abstract away complex API calls and focus on building applications Access all Noxus platform capabilities through a unified interface Built-in support for asynchronous operations for better performance Full type hints and Pydantic models for better development experience ## Key Features Create, manage, and execute complex AI workflows with visual node-based programming. Connect different AI models, data sources, and logic components to build sophisticated automation. Build intelligent chatbots and conversational interfaces with support for multiple AI models, custom tools, and context management. Create and manage knowledge repositories with advanced retrieval capabilities, document processing, and semantic search. Deploy autonomous AI agents that can perform tasks, use tools, and interact with users on your behalf. ## Who Should Use This SDK? Build AI-powered applications with minimal boilerplate code Create and deploy ML workflows and knowledge systems Integrate AI capabilities into existing products and services ## Quick Example Here's a taste of what you can do with the Noxus Client SDK: ```python theme={null} from noxus_sdk.client import Client from noxus_sdk.resources.conversations import ConversationSettings # Initialize the client client = Client(api_key="your_api_key_here") # Create a conversation with AI settings = ConversationSettings( model=["gpt-4o-mini"], temperature=0.7, max_tokens=150, extra_instructions="Be helpful and concise." ) conversation = client.conversations.create( name="My First Conversation", settings=settings ) # Send a message and get a response from noxus_sdk.resources.conversations import MessageRequest message = MessageRequest(content="Hello! How can AI help my business?") response = conversation.add_message(message) print(response.message_parts) ``` ## Getting Started Ready to dive in? Here's how to get started: Install the Noxus Client SDK using pip `bash pip install noxus-sdk ` Create an API key from your workspace control Set up your client and start building Try workflows, conversations, knowledge bases, and agents Follow our quickstart guide to build your first AI application in minutes ## Need Help? Comprehensive guides and API reference Join our community for help and discussions Get direct help from our support team Report bugs and request features # Connecting a client Source: https://docs.noxus.ai/sdk/mcp/connecting Configure Claude, Cursor, and other MCP clients to use the Noxus MCP server The Noxus MCP server is a remote **streamable-HTTP** endpoint at `https://backend.noxus.ai/mcp`, authenticated with your Noxus API key as a bearer token. Below are configs for the common clients. Replace `YOUR_NOXUS_API_KEY` with a key from **Settings → Organization → Workspaces → API Keys**. Your API key grants access to its workspace. Treat it like a password — prefer an environment variable over hardcoding it, and use a key scoped to only the permissions the client needs. Add the server with the CLI (HTTP transport + an Authorization header): ```bash theme={null} claude mcp add --transport http noxus https://backend.noxus.ai/mcp \ --header "Authorization: Bearer YOUR_NOXUS_API_KEY" ``` Then in a session, the Noxus tools are available to the model. Manage it with `claude mcp list` / `claude mcp remove noxus`. Claude Desktop can add remote MCP servers as **custom connectors**: **Settings → Connectors → Add custom connector**, then enter the URL `https://backend.noxus.ai/mcp`. When prompted for authentication, supply your API key as the bearer token. If your version needs a config-file entry instead, bridge the remote server with `mcp-remote` in `claude_desktop_config.json`: ```json theme={null} { "mcpServers": { "noxus": { "command": "npx", "args": [ "-y", "mcp-remote", "https://backend.noxus.ai/mcp", "--header", "Authorization: Bearer YOUR_NOXUS_API_KEY" ] } } } ``` Restart Claude Desktop after editing the file. Add to `.cursor/mcp.json` (project) or `~/.cursor/mcp.json` (global): ```json theme={null} { "mcpServers": { "noxus": { "url": "https://backend.noxus.ai/mcp", "headers": { "Authorization": "Bearer YOUR_NOXUS_API_KEY" } } } } ``` Enable it under **Cursor Settings → MCP**. Any client that supports the **streamable-HTTP** transport can connect. Provide: * **URL**: `https://backend.noxus.ai/mcp` * **Header**: `Authorization: Bearer YOUR_NOXUS_API_KEY` The server advertises its tools on connect (`tools/list`); no per-tool setup is needed. ## Self-hosted deployments On-prem / self-hosted Noxus serves the same MCP server at your backend host: ``` https:///mcp ``` Use that URL in any of the configs above. ## Verify the connection Once connected, ask the model to list something read-only, e.g. *"list my Noxus workflows"* or *"what knowledge bases do I have?"* — it should call `workflows_list` / `knowledge_bases_list` and return your workspace's resources. If calls fail with an auth error, re-check the bearer token; if a specific tool is refused, the key likely lacks that permission (see [Available tools](/sdk/mcp/tools#permissions)). # MCP Server Source: https://docs.noxus.ai/sdk/mcp/overview Drive the Noxus platform from any MCP-compatible client (Claude, Cursor, your own agents) ## What it is Noxus runs a hosted **[Model Context Protocol](https://modelcontextprotocol.io) (MCP) server**. It exposes the platform's capabilities — workflows, agents, conversations, knowledge bases, tables, sandboxes, deployments and more — as MCP **tools** that any MCP-compatible client can call. Point Claude Desktop, Claude Code, Cursor, or your own agent at it, authenticate with a Noxus API key, and the model can list your workflows, run them, search a knowledge base, query a data table, or create an agent — all as native tool calls, no glue code. A single streamable-HTTP endpoint — nothing to install or run locally. Authenticate with a workspace API key; tools are gated by that key's permissions. Backed by the same platform APIs as the Python SDK, so behavior matches. Tools carry typed schemas, so models call them reliably without prompt glue. ## Connection details | | | | ------------- | -------------------------------------------- | | **Endpoint** | `https://backend.noxus.ai/mcp` | | **Transport** | Streamable HTTP | | **Auth** | `Authorization: Bearer ` | | **Scope** | The workspace the API key belongs to | Self-hosted / on-prem deployments serve the same MCP server at `https:///mcp`. Use that URL in place of `backend.noxus.ai/mcp` below. ## Authentication The server takes your **Noxus API key** as a bearer token. It's the same key you use for the [SDK](/sdk/concepts/authentication) and [REST API](/api-reference/introduction) — create one in the Noxus UI under **Settings → Organization → Workspaces → (pick a workspace) → API Keys**. Every tool call runs **as that key**, in its workspace, and is subject to the key's permissions. A read-only key can list and read but not create or delete; a key without the `sandboxes:run` permission can't spin up sandboxes. This is the same permission model as the REST API — see [Authentication](/sdk/concepts/authentication). ## MCP vs. SDK vs. REST All three hit the same platform. Pick by how you're building: You want an **LLM/agent** to operate Noxus as tools (Claude, Cursor, a custom agent). No code — the model calls tools directly. You're writing a **Python program** and want typed resources, pagination helpers, and streaming. See the [SDK](/sdk/introduction). You're in **another language** or wiring a webhook/integration. See the [API reference](/api-reference/introduction). ## Next steps Config for Claude Desktop, Claude Code, Cursor, and generic MCP clients. The full catalog of tools the server exposes, by domain. Looking to give **one of your Noxus agents** access to an *external* MCP server (GitHub, Linear, Notion…)? That's the reverse direction — configured per-agent as an MCP tool. See the agent tools documentation. # Available tools Source: https://docs.noxus.ai/sdk/mcp/tools The tools the Noxus MCP server exposes, grouped by domain The server advertises its full tool list on connect, so you rarely need to name tools yourself — the model picks them. This page is a reference for what's available and how access is gated. Tool names and schemas may evolve; the live `tools/list` from your connection is always authoritative. Every tool runs **as your API key**, in its workspace. A tool the key lacks permission for is refused — see [Permissions](#permissions). ## Workflows Build, version, and run flows. * **Manage** — `workflows_list`, `workflows_get`, `workflows_update`, `workflows_delete`, `workflows_list_versions`, `workflows_save_version` * **Run** — `workflows_run`, `workflows_run_partial`, `workflows_list_runs` * **Author (node graph)** — `workflows_build`, `workflows_get_state`, `workflows_edit_nodes`, `workflows_add_node`, `workflows_link_nodes`, `workflows_remove_node`, `workflows_validate`, `workflows_autolayout`, `workflows_create_trigger`, `workflows_list_variables` * **Node catalog** — `workflows_get_node_schemas`, `workflows_search_nodes`, `workflows_get_node_outputs` ## Runs Inspect executions. * `runs_list`, `runs_get`, `runs_get_status`, `runs_wait`, `runs_inspect`, `runs_get_node` ## Agents (co-workers) * `agents_list`, `agents_get`, `agents_create`, `agents_update`, `agents_delete` * `agents_list_triggers`, `agents_add_trigger` ## Conversations Chat with an agent and read history. * `conversations_list`, `conversations_get`, `conversations_create`, `conversations_update`, `conversations_delete` * `conversations_chat`, `conversations_get_messages`, `conversations_inspect_messages` ## Knowledge bases Manage KBs and their documents, and retrieve from them. * **Manage** — `knowledge_bases_list`, `knowledge_bases_get`, `knowledge_bases_create`, `knowledge_bases_delete` * **Documents** — `knowledge_bases_upload_documents`, `knowledge_bases_list_documents`, `knowledge_bases_get_document`, `knowledge_bases_delete_document`, `knowledge_bases_export_documents`, `knowledge_bases_get_runs`, `knowledge_bases_get_tree`, `knowledge_bases_list_folder` * **Search** — `knowledge_bases_search`, `knowledge_bases_search_documents` ## Data tables Structured rows + read-only SQL. * **Schema** — `tables_list`, `tables_get_schema`, `tables_create`, `tables_add_column` * **Rows** — `tables_get_rows`, `tables_insert_rows`, `tables_update_rows`, `tables_delete_rows` * **Query** — `tables_query` (read-only SQL across the workspace's tables) ## Sandboxes Ephemeral code-execution environments. * `sandboxes_create`, `sandboxes_list`, `sandboxes_get`, `sandboxes_delete` * `sandboxes_run_command`, `sandboxes_write_file`, `sandboxes_read_file` ## Deployments (channels) Publish an agent to a channel. * `deployments_list_channels`, `deployments_list`, `deployments_get`, `deployments_create`, `deployments_activate`, `deployments_deactivate`, `deployments_delete` ## Files * `files_upload`, `files_download` ## Workspaces & admin * `workspaces_list` * `admin_get_me`, `admin_list_workspaces`, `admin_create_workspace` ## Documentation Let the model look things up in the Noxus docs. * `documentation_list_sections`, `documentation_read`, `documentation_search` ## Permissions Tools are gated by the **permissions of the API key** you connected with — identical to the REST API. A few consequences worth knowing: * A **read-only** key can list/read/search but can't create, edit, or delete. * Running code in a sandbox requires the **`sandboxes:run`** permission (it is never implied by general resource access). * Tenant-wide actions (creating workspaces, managing users) require a **system API key** — an ordinary workspace key can't perform them. If a tool is refused, the connected key is missing that permission — mint a key with the needed scope, or use a key from a workspace where you have the role. See [Authentication](/sdk/concepts/authentication). # Quick Start Source: https://docs.noxus.ai/sdk/quickstart Get up and running with the Noxus Client SDK in minutes ## Prerequisites Before you begin, make sure you have: * **Python 3.10 or later** installed on your system * A **Noxus account** with access to a workspace * An **API key** from your Noxus workspace Don't have a Noxus account yet? [Sign up here](https://app.noxus.ai/signup) to get started. ## Installation Install the Noxus Client SDK using pip: ```bash Terminal theme={null} pip install noxus-sdk ``` ```bash Poetry theme={null} poetry add noxus-sdk ``` ```bash Pipenv theme={null} pipenv install noxus-sdk ``` The SDK requires Python 3.10 or later and automatically installs all necessary dependencies. ## Get Your API Key Log in to your Noxus account and navigate to your workspace Go to **Settings → Organization → Workspaces** and select your workspace Navigate to the **API Keys** tab and create a new API key Copy the generated API key - you'll need it for authentication Keep your API key secure and never commit it to version control. Consider using environment variables to store it safely. ## Initialize the Client Create your first Noxus client: ```python theme={null} from noxus_sdk.client import Client # Initialize with your API key client = Client(api_key="your_api_key_here") # Or use environment variable import os client = Client(api_key=os.getenv("NOXUS_API_KEY")) # For custom backend URL (if needed) client = Client( api_key="your_api_key_here", base_url="https://your-custom-backend.com" ) ``` You can also set the `NOXUS_BACKEND_URL` environment variable instead of passing `base_url` directly. ## Your First Conversation Let's create a simple AI conversation: ```python theme={null} from noxus_sdk.client import Client from noxus_sdk.resources.conversations import ConversationSettings, MessageRequest # Initialize the client client = Client(api_key="your_api_key_here") # Configure conversation settings settings = ConversationSettings( model=["gpt-4o-mini"], temperature=0.7, max_tokens=150, tools=[], extra_instructions="Be helpful and concise." ) # Create a new conversation conversation = client.conversations.create( name="My First Conversation", settings=settings ) print(f"Created conversation: {conversation.id}") # Send a message message = MessageRequest(content="Hello! What can you help me with?") response = conversation.add_message(message) print(f"AI Response: {response.message_parts}") ``` ## Your First Workflow Now let's create a simple workflow: ```python theme={null} from noxus_sdk.workflows import WorkflowDefinition # Create a workflow definition workflow_def = WorkflowDefinition(name="Hello World Workflow") # Add nodes input_node = workflow_def.node("InputNode").config( label="User Input", fixed_value=True, value="Tell me a fun fact about space", type="str" ) ai_node = workflow_def.node("TextGenerationNode").config( template="Answer this question: ((Input 1))", model=["gpt-4o-mini"] ) output_node = workflow_def.node("OutputNode") # Connect the nodes workflow_def.link(input_node.output(), ai_node.input("variables", "Input 1")) workflow_def.link(ai_node.output(), output_node.input()) # Save the workflow workflow = client.workflows.save(workflow_def) print(f"Created workflow: {workflow.id}") # Run the workflow run = workflow.run(body={}) result = run.wait(interval=2) print(f"Workflow result: {result.output}") ``` ## Explore Platform Capabilities Get information about available models and nodes: ```python theme={null} # List available AI models models = client.get_models() print("Available models:") for model in models[:3]: # Show first 3 print(f"- {model['name']}") # List available workflow nodes nodes = client.get_nodes() print(f"\nAvailable node types: {len(nodes)}") for node in nodes[:5]: # Show first 5 print(f"- {node['type']}") # Get chat presets presets = client.get_chat_presets() print(f"\nAvailable chat presets: {len(presets)}") ``` ## Working with Knowledge Bases Create a knowledge base for document storage and retrieval: ```python theme={null} from noxus_sdk.resources.knowledge_bases import ( KnowledgeBaseSettings, KnowledgeBaseIngestion, KnowledgeBaseRetrieval ) # Configure knowledge base settings settings = KnowledgeBaseSettings( ingestion=KnowledgeBaseIngestion( batch_size=10, default_chunk_size=1000, default_chunk_overlap=200, enrich_chunks_mode="contextual" ), retrieval=KnowledgeBaseRetrieval( type="hybrid_reranking", hybrid_settings={"fts_weight": 0.3} ) ) # Create knowledge base kb = client.knowledge_bases.create( name="My Knowledge Base", description="A sample knowledge base for testing", document_types=["pdf", "txt", "docx"], settings_=settings ) print(f"Created knowledge base: {kb.id}") ``` ## Error Handling Always include proper error handling in your applications: ```python theme={null} from noxus_sdk.client import Client import httpx try: client = Client(api_key="your_api_key_here") workflows = client.workflows.list() print(f"Found {len(workflows)} workflows") except httpx.HTTPStatusError as e: if e.response.status_code == 401: print("Authentication failed. Check your API key.") elif e.response.status_code == 403: print("Access denied. Check your permissions.") else: print(f"HTTP error: {e.response.status_code}") except httpx.RequestError as e: print(f"Network error: {e}") except Exception as e: print(f"Unexpected error: {e}") ``` ## Next Steps Learn to build complex AI workflows with visual programming Create intelligent chatbots with tools and context Set up knowledge bases for document retrieval Create autonomous AI agents for your applications ## Example Projects ```python theme={null} # Create a support bot with knowledge base integration from noxus_sdk.resources.conversations import ( ConversationSettings, KnowledgeBaseQaTool ) kb_tool = KnowledgeBaseQaTool( enabled=True, kb_id="your_kb_id", extra_instructions="Provide helpful support answers" ) settings = ConversationSettings( model=["gpt-4o-mini"], tools=[kb_tool], extra_instructions="You are a helpful customer support agent." ) support_bot = client.conversations.create( name="Support Bot", settings=settings ) ``` ````python # Create a workflow that processes and summarizes documents theme={null} workflow_def = WorkflowDefinition(name="Document Processor") input_node = workflow_def.node("InputNode").config( label="Document Input" ) summarizer = workflow_def.node("SummaryNode").config( summary_format="Bullet Points", summary_topic="Key insights and action items" ) output_node = workflow_def.node("OutputNode") workflow_def.link(input_node.output(), summarizer.input()) workflow_def.link(summarizer.output(), output_node.input()) processor = client.workflows.save(workflow_def) ``` ```python # Create an agent that can research topics using web search from noxus_sdk.resources.conversations import WebResearchTool web_tool = WebResearchTool( enabled=True, extra_instructions="Focus on recent and reliable sources" ) agent_settings = ConversationSettings( model=["gpt-4o-mini"], tools=[web_tool], extra_instructions="You are a research assistant. Provide well-sourced information." ) research_agent = client.agents.create( name="Research Assistant", settings=agent_settings ) ```` ## Get Help Having trouble? Check out our [troubleshooting guide](/sdk/guides/troubleshooting) or reach out to [support@noxus.ai](mailto:support@noxus.ai). # Admin, Variables, Files & Analytics Source: https://docs.noxus.ai/sdk/resources/admin Manage workspaces, users, roles, system keys, variables, files, and usage metrics Cross-cutting workspace and tenant management, plus variables/secrets, raw file transfer, and usage analytics. ## `client.admin` — workspaces, users, roles, system keys `get_me` tells you who the key is; `client.admin.enabled` is set from it when `load_me=True`. **Tenant-level** operations (users, workspaces, roles, system keys) require a **tenant-admin / system key**, not an ordinary workspace key. ```python theme={null} me = client.admin.get_me() # -> ApiKey: id, name, tenant_admin, value, permissions ``` ### Workspaces ```python theme={null} client.admin.list_workspaces() # -> list[Workspace] ws = client.admin.create_workspace("Marketing", description="…") # -> Workspace ws.delete() # on the Workspace object ws.add_api_key(...) # mint a workspace key ``` `Workspace` fields: `id`, `name`, `description`. ### Users (tenant) ```python theme={null} client.admin.list_users() # -> list[TenantUser]: id, email, display_name, tenant_admin, is_active ``` ### Roles ```python theme={null} client.admin.list_roles() # -> list[dict] client.admin.create_role("Editor", permissions={"resource:edit": True}, description="…") client.admin.delete_role(role_id) ``` ### System keys (tenant-scoped) System keys live in the tenant's hidden **admin** workspace and may carry tenant-wide permissions (`users:*`, `workspace:*`, `providers:manage`, `org:*`, `billing:manage`) — ordinary workspace keys cannot. Mint them here: ```python theme={null} client.admin.list_system_keys() # -> list[ApiKey] key = client.admin.create_system_key( "ci-bot", permissions=["users:read", "workspace:read"], tenant_admin=False ) print(key.value) # the secret — shown once client.admin.delete_system_key(key.id) ``` `ApiKey` fields: `id`, `name`, `tenant_admin`, `value`, `permissions`. ## `client.variables` — variables & secrets Workspace-scoped key/value config. `kind="secret"` values are write-only (never returned on read). ```python theme={null} client.variables.list() # -> list[dict] (secret values stripped) client.variables.create( "API_BASE", value="https://api.example.com", kind="variable", # "variable" | "secret" value_type="string", # string | number | boolean | json | datetime | file source="inline", # inline | vault | environment ) client.variables.update(variable_id, {"value": "…"}) client.variables.delete(variable_id) # -> bool # escape hatch for the full payload shape: client.variables.create_raw({"name": "X", "value": "…", "kind": "variable"}) ``` ## `client.files` — raw file transfer ```python theme={null} with open("report.pdf", "rb") as fd: f = client.files.save(fd) # -> File (has an id/spot-uri) data = client.files.get(f.id) # -> bytes ``` Files are how you pass binary content to workflow/KB inputs that expect a file. ## `client.analytics` — usage metrics ```python theme={null} from datetime import datetime, timedelta end = datetime.utcnow() start = end - timedelta(days=30) result = client.analytics.get("runs", start, end, page=1, page_size=50) # -> AnalyticsResult ``` `metric` is the platform metric name; the window is `[time_start, time_end]`. Agent-specific dashboards (CSAT, topics, sentiment) live under `client.insights` instead — see [Agent Insights](/sdk/resources/insights). All methods here have `a`-prefixed async twins. # Agents Source: https://docs.noxus.ai/sdk/resources/agents Create, configure, version, and export AI agents with the Noxus SDK `client.agents` manages agents ("co-workers") — configurable AI assistants with tools, personas, and their own versioned lifecycle. Once created, you chat with them through [conversations](/sdk/conversations/overview) and read their analytics through [insights](/sdk/resources/insights). ## Creating an agent An agent is configured with `AgentSettings`, which controls its model, behavior, and available tools. ```python theme={null} from noxus_sdk.resources.assistants import AgentSettings settings = AgentSettings( model=["gpt-4o-mini"], temperature=0.7, max_tokens=4000, tools=[], # required (empty list = no tools) extra_instructions="Be concise and friendly.", persona=None, tone=None, # optional ) agent = client.agents.create(name="Support Bot", settings=settings) ``` `AgentSettings` fields: `model: list[str]`, `temperature: float`, `max_tokens` (default 64000), `tools: list[...]` (required — the discriminated tool union: web\_research, kb\_qa, workflow, code\_execution, sandbox, …), `persona`, `tone`, `extra_instructions`, `agent_flow_id`. ## CRUD & lifecycle ```python theme={null} client.agents.list() # -> list[Agent] client.agents.get(agent_id) # -> Agent client.agents.update(agent_id, name=None, settings=None, preview=False) client.agents.delete(agent_id) client.agents.duplicate(agent_id) # -> new Agent client.agents.publish(agent_id) # publish the current draft as a version client.agents.restore(agent_id) # restore last published client.agents.list_versions(agent_id, page=1, page_size=10) client.agents.get_tool_schemas() # -> dict of available tool configs ``` `update(..., preview=True)` returns what the change *would* look like without saving. From an `Agent` object you can call `.update(name, settings)`, `.delete()`, and `.triggers()` / `.add_trigger(trigger_data)` directly. ## Export / import ```python theme={null} blob = client.agents.export(agent_id, version="auto", version_id=None, set_active_on_import=False) # -> bytes client.agents.export_preview(agent_id) # -> dict client.agents.import_(blob, version="auto", mode="clone", activate=False, dry_run=False) # -> list[dict] ``` `mode` is `"clone" | "version" | "replace"`; `dry_run=True` validates only. ## Triggers Agent triggers (as opposed to workflow triggers) are managed directly from an `Agent` object: ```python theme={null} agent = client.agents.get(agent_id) agent.add_trigger(trigger_data) agent.triggers() ``` For workflow triggers and deployment channels, see [Deployments & Triggers](/sdk/resources/deployments). # Deployments & Triggers Source: https://docs.noxus.ai/sdk/resources/deployments Publish agents to channels and manage workflow triggers with the Noxus SDK `client.deployments` publishes an agent to a **channel** (embed widget, Slack, form, …). `client.triggers` reads and manages **workflow** triggers and the events they receive. ## Deployments A deployment is "this agent, on this channel, with this config". It starts inactive; **activate** builds its trigger and goes live. ### Discover channels ```python theme={null} for ch in client.deployments.list_channels(): print(ch["channel_type"], ch["label"]) # channel_type is what you pass to create() ``` ### Create → activate ```python theme={null} dep = client.deployments.create( agent_id, channel_type="embed_widget", # from list_channels() name="Website widget", alias="acme-support", # optional stable public handle config={}, # channel-specific, validated server-side assistant_version_id=None, # pin a version (required before activate) ) client.deployments.activate(agent_id, dep["id"]) # publish (needs a pinned version) ``` Deployments and their `config` are returned as plain dicts (secrets redacted). Activation requires `assistant_version_id` to be set and the agent version to be valid. ### Manage ```python theme={null} client.deployments.list(agent_id) # -> list[dict] client.deployments.get(agent_id, deployment_id) client.deployments.update(agent_id, deployment_id, {"name": "New name"}) # patch; alias:None clears it client.deployments.deactivate(agent_id, deployment_id) client.deployments.delete(agent_id, deployment_id) # -> bool (deactivates first) ``` Mutating an **active** deployment's `config` or `assistant_version_id` rebuilds its trigger automatically. ### Events The events the deployment's trigger has received (deliveries, failures): ```python theme={null} client.deployments.list_events(agent_id, deployment_id, page=1, page_size=10) for event in client.deployments.iter_events(agent_id, deployment_id): # auto-paginate print(event) ``` Async twins throughout: `acreate`, `aactivate`, `adeactivate`, `aupdate`, `adelete`, `alist`, `aget`, `alist_channels`, `alist_events`, `aiter_events`. ## Triggers (workflow) Triggers fire a **workflow** on an external event (schedule, webhook, …). ```python theme={null} # read client.triggers.list(workflow_id, page=1, page_size=10) # -> list[dict] client.triggers.list_events(workflow_id, trigger_id, search=None) for ev in client.triggers.iter_events(workflow_id, trigger_id): # auto-paginate print(ev) # all events across the workspace, filtered client.triggers.events(event_type="webhook", workflow_id=workflow_id, started_run=True) # create / update / delete client.triggers.create(workflow_id, definition={"type": "schedule", ...}, workflow_version_id="v-1") client.triggers.update(workflow_id, trigger_id, definition={...}, workflow_version_id=None) client.triggers.delete(trigger_id) # -> bool ``` `definition` is the trigger config dict (its shape depends on `type`). A trigger is pinned to a `workflow_version_id`. Every method has an `a`-prefixed async twin. Agent triggers (as opposed to workflow triggers) are managed from an `Agent` object: `agent.add_trigger(trigger_data)` and `agent.triggers()`. See [Agents](/sdk/resources/agents). # Agent Insights Source: https://docs.noxus.ai/sdk/resources/insights Read agent conversation analytics — topics, sentiment, CSAT, and more `client.insights` reads read-only analytics computed over an [agent's](/sdk/resources/agents) conversations — topics, sentiment, CSAT, rating drivers, and auto-surfaced highlights. ## Metrics Every metric takes an `agent_id` and optional `days` (window), `deployment_id` (scope to one channel), and `message_length` (`"short" | "medium" | "long"`). All return `dict`s. ```python theme={null} client.insights.bootstrap_status(agent_id) # is analytics ready yet? client.insights.top_topics(agent_id, days=30, limit=10) client.insights.sub_topics(agent_id, parent="billing", days=30) client.insights.csat_score(agent_id, days=7) client.insights.sentiment_over_time(agent_id, days=30) client.insights.rating_drivers(agent_id, days=30, limit=8) client.insights.conversation_funnel(agent_id, days=30) client.insights.custom_insights(agent_id, days=30, limit=20) client.insights.noticed(agent_id, limit=12) # auto-surfaced highlights ``` ## Drilling into conversations Drill from a chart into the underlying conversations: ```python theme={null} client.insights.conversations( agent_id, kind="topic", key="billing", days=30, limit=20, ) # kind: "topic" | "subtopic" | "driver" | "custom" | "cx" ``` Each metric has an `a`-prefixed async twin (`atop_topics`, `acsat_score`, …). # Knowledge Bases Source: https://docs.noxus.ai/sdk/resources/knowledge-bases Ingest, search, and manage vector-backed knowledge bases with the Noxus SDK `client.knowledge_bases` manages KBs and their documents: ingestion (upload & train), listing, semantic search, and export/import. A KB stores documents as vectors so agents and workflows can retrieve from them. ## Create a KB Use a **v3** config (the current format). `KBConfigV3` has sensible defaults, so you usually only set the embedding model if you need a specific one. ```python theme={null} from noxus_sdk.resources.knowledge_bases import KBConfigV3 kb = client.knowledge_bases.create( name="Product Docs", description="Everything about the product", document_types=["pdf", "docx", "txt", "md"], settings_=KBConfigV3(), # defaults: multilingual embeddings, 2048/512 chunks version="v3", ) print(kb.id) ``` `KBConfigV3` fields: `embedding_model: list[str]` (default `["vertexai/text-multilingual-embedding-002"]`), `default_chunk_size` (2048), `default_chunk_overlap` (512), `csv_row_as_document` (True). ## Manage KBs ```python theme={null} client.knowledge_bases.list(page=1, page_size=10) # -> list[KnowledgeBase] client.knowledge_bases.get(kb_id) client.knowledge_bases.update(kb_id, name=None, description=None, document_types=None) client.knowledge_bases.delete(kb_id) # -> bool ``` ## Adding documents **Upload local files** (they ingest & train asynchronously — returns run ids): ```python theme={null} run_ids = client.knowledge_bases.upload_document( kb_id, files=["./guide.pdf", "./faq.md"], prefix="/manuals" ) # wait for ingestion to finish: for run in client.knowledge_bases.get_runs(kb_id, run_ids=",".join(run_ids)): run.wait() ``` **Train from a source** (URL / connector / etc. via a `Source`): ```python theme={null} from noxus_sdk.resources.knowledge_bases import Source client.knowledge_bases.train_document(kb_id, source=Source(...), prefix="/") ``` **Create a bare document row** (e.g. a folder or a placeholder): ```python theme={null} from noxus_sdk.resources.knowledge_bases import CreateDocument doc = client.knowledge_bases.create_document( kb_id, CreateDocument(name="notes.txt", prefix="/misc") ) ``` ## Listing & iterating documents `status` is one of `trained | training | error | uploaded | folder`. Omit it to list **all** statuses (the SDK loops them for you). Prefer the iterator to walk every document without hand-rolling pages: ```python theme={null} # one page, one status client.knowledge_bases.list_documents(kb_id, status="trained", page=1, page_size=10) # every document, every status, auto-paginated for doc in client.knowledge_bases.iter_documents(kb_id): print(doc.name, doc.status, doc.size, doc.content_type) # only documents mid-ingestion client.knowledge_bases.list_ingestion_documents(kb_id) ``` `KnowledgeBaseDocument` fields: `id`, `name`, `prefix`, `status`, `size`, `source_type`, `file_id`, `content_type`, `created_at`, `updated_at`, `error`. ## Document operations ```python theme={null} client.knowledge_bases.get_document(kb_id, document_id) client.knowledge_bases.download_document(kb_id, document_id) # -> bytes client.knowledge_bases.update_document(kb_id, document_id, UpdateDocument(prefix="/new")) client.knowledge_bases.delete_document(kb_id, document_id) client.knowledge_bases.dismiss_document(kb_id, document_id) # ignore an errored doc client.knowledge_bases.retry_document(kb_id, document_id) # re-ingest one client.knowledge_bases.retry_all(kb_id) # re-ingest all failed ``` ## Search **Semantic search** returns scored chunks: ```python theme={null} for hit in client.knowledge_bases.search(kb_id, query="refund window", prefix="/"): print(hit.score, hit.content, hit.source) # SearchResult: score, content, source, document_source ``` **Document search** returns matching documents (metadata), not chunks: ```python theme={null} client.knowledge_bases.search_documents(kb_id, query="invoice", limit=25) ``` ## Structure, types & export ```python theme={null} client.knowledge_bases.get_tree(kb_id, folder="/", max_depth=3) # nested folder view client.knowledge_bases.list_folder(kb_id, folder="/manuals") client.knowledge_bases.get_types() # supported document types client.knowledge_bases.get_mime_types() # supported MIME types blob = client.knowledge_bases.export(kb_id, version="auto", set_active_on_import=False) client.knowledge_bases.import_(blob, version="auto", mode="clone", dry_run=False) ``` `version` is `"auto" | "v3" | "v4"`; `mode` is `"clone" | "version" | "replace"`. ## Recipe: export a KB's documents to a spreadsheet ```python theme={null} import openpyxl wb = openpyxl.Workbook(); ws = wb.active ws.append(["name", "status", "size", "content_type", "created_at"]) for doc in client.knowledge_bases.iter_documents(kb_id): ws.append([doc.name, doc.status, doc.size, doc.content_type, doc.created_at]) wb.save("kb_documents.xlsx") ``` # Runs Source: https://docs.noxus.ai/sdk/resources/runs Execute workflows and read their results with the Noxus SDK runs service Once you've built and saved a workflow (see [Building Workflows](/sdk/workflows/building-workflows)), you execute it and read its results either from the definition object or through `client.runs`. This page covers both. ## Running a workflow Two ways to execute: **1. From the definition object → a `Run` you poll or stream** ```python theme={null} wf = client.workflows.get(workflow_id) run = wf.run({"Input 1": "hello"}) # -> Run (queued) result = run.wait(output_only=True) # blocks (polls every 5s), returns output # or step through progress events: for event in wf.run_and_stream({"Input 1": "hello"}): print(event) ``` **2. Synchronous one-shot via the runs service** ```python theme={null} out = client.runs.run_sync(workflow_id, {"Input 1": "hello"}, output_only=True) ``` `body`/`input` is a dict keyed by the workflow's input labels. For input formats (node labels, node IDs, files), streaming, and webhook callbacks, see [Running Workflows](/sdk/workflows/running-workflows). ## The runs service ```python theme={null} client.runs.list(workflow_id, page=1, page_size=10) # -> list[Run] client.runs.get(workflow_id, run_id) # -> Run client.runs.run_sync(workflow_id, input, output_only=False) client.runs.stop(run_id) # -> Run (cancel) client.runs.get_data(run_id, fetch_structured_data=True) # full run payload client.runs.get_node_io(run_id, node_id, it=0) # a node's inputs/outputs client.runs.search("invoice", limit=10, exact=True, search_in=None) # search across runs ``` ## The `Run` object Fields: `id`, `status`, `progress`, `progress_details`, `workflow_id`, `input`, `output`, `created_at`, `finished_at`. Methods (each with an `a`-prefixed async twin): ```python theme={null} run.wait(interval=5, output_only=False) # poll until terminal; returns Run or output dict run.get_status() # -> str run.refresh() # re-fetch run.stop() # cancel run.data(fetch_structured_data=True) # full payload for event in run.stream(etag=None): # live RunEvents print(event) ``` `wait(output_only=True)` returns just the output dict; otherwise it returns the refreshed `Run`. Streaming yields `RunEvent`s as the run progresses. # Sandboxes Source: https://docs.noxus.ai/sdk/resources/sandboxes Run untrusted code in ephemeral, network-jailed execution environments `client.sandboxes` gives you ephemeral, network-jailed code-execution environments (gVisor microVMs) with `noxus-sdk` pre-installed. Use them to run untrusted or one-off code — data processing, generating a file, running an SDK script against the platform. ## Lifecycle ```python theme={null} sb = client.sandboxes.create(label="my-job", persistent=False) # -> Sandbox client.sandboxes.list() # -> list[Sandbox] client.sandboxes.get(sandbox_id) client.sandboxes.delete(sandbox_id) # -> bool sb.kill() # same as delete, on the object sb.refresh() # re-fetch status ``` `persistent=False` sandboxes are cheap and cleaned up when idle; pass `persistent=True` only if you need it to survive between calls. `Sandbox` fields: `id`, `status`, `created_at`, `last_activity`, `label`. ## Prefer the context manager — it always tears down ```python theme={null} with client.sandboxes.create(label="job") as sb: sb.files.write("/work/data.json", '{"n": 1}') result = sb.commands.run("cat /work/data.json") print(result.stdout) # sandbox is killed on exit, even if the block raises async with await client.sandboxes.acreate() as sb: # async form ... ``` ## Run commands ```python theme={null} result = sb.commands.run("python -c 'print(2+2)'", timeout=60) # Execution fields: result.stdout # str result.stderr # str result.exit_code # int result.timed_out # bool ``` ## Read & write files ```python theme={null} sb.files.write("/work/script.py", "print('hi')") # str or bytes sb.files.write("/work/blob.bin", b"\x00\x01") content = sb.files.read("/work/script.py") # -> str raw = sb.files.read_bytes("/work/blob.bin") # -> bytes ``` ## Recipe: run an SDK script inside the sandbox The sandbox can call the Noxus API itself (it has `noxus_sdk` baked in). Inject a scoped key and backend URL as env vars, then run the script: ```python theme={null} script = """ import os from noxus_sdk.client import Client c = Client.from_env() print([kb.name for kb in c.knowledge_bases.list()]) """ with client.sandboxes.create(label="sdk-run") as sb: sb.files.write("/work/run.py", script) out = sb.commands.run( "export NOXUS_API_KEY=... NOXUS_BACKEND_URL=https://backend.noxus.ai; " "python /work/run.py" ) print(out.stdout, "exit", out.exit_code) ``` The sandbox network jails RFC1918 but NATs out, so the backend must be reachable at a **public** URL from inside the sandbox for the SDK-in-sandbox pattern to work. Async twins exist throughout: `client.sandboxes.acreate` / `alist` / `aget` / `adelete`; `sb.akill` / `sb.arefresh`; `sb.commands.arun`; and `sb.files.awrite` / `aread` / `aread_bytes`. # Data Tables Source: https://docs.noxus.ai/sdk/resources/tables Store, query, and manage structured workspace data tables with the Noxus SDK `client.tables` manages workspace data tables — structured rows you can insert, update, query with SQL, and import/export as CSV. Great for staging data a workflow or agent will read. ## Create a table ```python theme={null} from noxus_sdk.resources.tables import TableColumn table = client.tables.create( name="Customers", columns=[ TableColumn(name="email", type="string"), TableColumn(name="signups", type="number"), {"name": "active", "type": "boolean"}, # dicts work too ], description="CRM export", id_type="uuid", # "uuid" (default) or "serial" (autoincrement) ) ``` Column `type` is one of `string | number | boolean | datetime | file`. Every table has an implicit `id` primary key (don't declare it). ## Manage tables ```python theme={null} client.tables.list() # -> list[Table] (incl. read-only platform views) client.tables.get(table_id) # -> Table client.tables.delete(table_id) # -> bool ``` ## Columns (on a `Table` object) ```python theme={null} table.add_column(name="phone", type="string", label="Phone") table.rename_column("phone", "mobile") table.drop_column("mobile") ``` ## Rows (on a `Table` object) Rows are plain dicts keyed by column name (`RowValues`). ```python theme={null} table.insert({"email": "a@b.com", "signups": 3, "active": True}) # -> the row table.insert_rows([{...}, {...}]) # bulk, -> count table.update_row(row_id, {"signups": 4}) table.delete_row(row_id) table.clear() # delete all rows -> count # read table.list_rows(limit=50, offset=0, search="a@b.com") for row in table.iter_rows(page_size=500, search=None): # auto-paginate every row print(row) ``` ## SQL query Run read-only SQL across the workspace's tables: ```python theme={null} result = client.tables.query("SELECT email, signups FROM customers WHERE active") print(result.columns) # list[str] for row in result.rows: # list[dict] print(row) ``` The query is guarded server-side (read-only, per-tenant). Use the table's `sql_name` (lowercased name) as the SQL identifier. ## Stats & CSV ```python theme={null} stats = table.stats() # TableStats: row_count, size_bytes, column_count csv_bytes = table.export_csv() # -> bytes ``` Every method has an `a`-prefixed async twin (`acreate`, `aquery`, `ainsert`, `aiter_rows`, …). # Building Workflows Source: https://docs.noxus.ai/sdk/workflows/building-workflows Learn how to create powerful AI workflows using the Noxus Client SDK ## Getting Started Building workflows with the Noxus SDK is a programmatic approach to creating visual AI automations. You define nodes, configure them, and connect them together to create sophisticated data processing pipelines. ## Basic Workflow Structure Every workflow starts with a `WorkflowDefinition`: ```python theme={null} from noxus_sdk.client import Client from noxus_sdk.workflows import WorkflowDefinition # Initialize client and create workflow definition client = Client(api_key="your_api_key_here") workflow_def = WorkflowDefinition(name="My First Workflow") ``` ## Adding Nodes Nodes are the building blocks of your workflow. Each node performs a specific function: ```python theme={null} # Add an input node input_node = workflow_def.node("InputNode").config( label="User Input", type="str", fixed_value=False # Allow dynamic input ) # Add an AI text generation node ai_node = workflow_def.node("TextGenerationNode").config( template="Please respond to: ((User Input))", model=["gpt-4o-mini"], temperature=0.7, max_tokens=150 ) # Add an output node output_node = workflow_def.node("OutputNode") ``` Node types are case-sensitive and must match exactly. Use `client.get_nodes()` to see all available node types. ## Node Configuration Each node type has specific configuration options. Here are some common patterns: ### Input Nodes ```python theme={null} # Dynamic input (user provides value at runtime) dynamic_input = workflow_def.node("InputNode").config( label="Question", type="str", fixed_value=False ) # Fixed input (value set at design time) fixed_input = workflow_def.node("InputNode").config( label="System Prompt", type="str", fixed_value=True, value="You are a helpful assistant." ) # File input file_input = workflow_def.node("FileInputNode").config( label="Document", accepted_types=["pdf", "txt", "docx"], max_size_mb=10 ) ``` ### AI Model Nodes ```python theme={null} # Text generation with template text_gen = workflow_def.node("TextGenerationNode").config( template="Answer this question: ((Question))\n\nContext: ((Context))", model=["gpt-4o-mini"], temperature=0.7, max_tokens=200, top_p=0.9 ) # Summary generation summarizer = workflow_def.node("SummaryNode").config( summary_format="Bullet Points", # or "Paragraph", "Key Points" summary_topic="Main insights and conclusions", max_length=300, language="English" ) # Translation translator = workflow_def.node("TranslationNode").config( target_language="Spanish", source_language="auto", # Auto-detect preserve_formatting=True ) ``` ### Data Processing Nodes ```python theme={null} # Compose multiple text inputs composer = workflow_def.node("ComposeTextNode").config( template="""# Report Title: ((Title)) ## Summary ((Summary)) ## Details ((Details)) Generated on: {{current_date}} """ ) # Extract text from files extractor = workflow_def.node("ExtractTextNode").config( preserve_formatting=True, extract_tables=True, extract_images=False ) # Filter data based on conditions filter_node = workflow_def.node("FilterNode").config( condition="length > 100", # Filter text longer than 100 characters filter_type="text_length" ) ``` ## Connecting Nodes Connections define how data flows between nodes. The basic pattern is: ```python theme={null} # Basic connection: output of one node to input of another workflow_def.link(source_node.output(), target_node.input()) # Named connections for specific inputs/outputs workflow_def.link( source_node.output("result"), target_node.input("data") ) # Variable inputs (for nodes that accept multiple named inputs) workflow_def.link( input_node.output(), ai_node.input("variables", "User Input") ) ``` ### Understanding Input Types Different nodes have different input requirements: Most nodes have a single input that accepts the previous node's output: ```python theme={null} # Simple chain: Input → AI → Output workflow_def.link(input_node.output(), ai_node.input()) workflow_def.link(ai_node.output(), output_node.input()) ``` Some nodes (like TextGenerationNode) accept multiple named variables: ````python # AI node with multiple variables ai_node = theme={null} workflow_def.node("TextGenerationNode").config( template="Compare ((Item 1)) with ((Item 2))" ) # Connect multiple inputs workflow_def.link(input1.output(), ai_node.input("variables", "Item 1")) workflow_def.link(input2.output(), ai_node.input("variables", "Item 2")) ``` Some nodes have specific named inputs: ```python # Conditional node with condition and data inputs conditional = workflow_def.node("ConditionalNode").config( condition="length > 100" ) workflow_def.link(text_input.output(), conditional.input("data")) workflow_def.link(condition_input.output(), conditional.input("condition")) ```` ## Complete Workflow Example Here's a complete example that creates a document analysis workflow: ```python theme={null} from noxus_sdk.client import Client from noxus_sdk.workflows import WorkflowDefinition # Initialize client = Client(api_key="your_api_key_here") workflow_def = WorkflowDefinition(name="Document Analyzer") # Step 1: Input nodes document_input = workflow_def.node("FileInputNode").config( label="Document to Analyze", accepted_types=["pdf", "txt", "docx"] ) analysis_type = workflow_def.node("InputNode").config( label="Analysis Type", type="str", fixed_value=True, value="sentiment and key themes" ) # Step 2: Extract text from document text_extractor = workflow_def.node("ExtractTextNode").config( preserve_formatting=True ) # Step 3: Create summary summarizer = workflow_def.node("SummaryNode").config( summary_format="Bullet Points", summary_topic="Key points and main ideas", max_length=200 ) # Step 4: Perform analysis analyzer = workflow_def.node("TextGenerationNode").config( template="""Analyze the following document for ((Analysis Type)): Document Text: ((Document Text)) Please provide: 1. Overall assessment 2. Key findings 3. Recommendations """, model=["gpt-4o-mini"], temperature=0.3, max_tokens=400 ) # Step 5: Combine results report_composer = workflow_def.node("ComposeTextNode").config( template="""# Document Analysis Report ## Document Summary ((Summary)) ## Detailed Analysis ((Analysis)) --- Report generated on {{current_date}} """ ) # Step 6: Output output_node = workflow_def.node("OutputNode") # Connect all nodes workflow_def.link(document_input.output(), text_extractor.input()) workflow_def.link(text_extractor.output(), summarizer.input()) workflow_def.link(text_extractor.output(), analyzer.input("variables", "Document Text")) workflow_def.link(analysis_type.output(), analyzer.input("variables", "Analysis Type")) workflow_def.link(summarizer.output(), report_composer.input("variables", "Summary")) workflow_def.link(analyzer.output(), report_composer.input("variables", "Analysis")) workflow_def.link(report_composer.output(), output_node.input()) # Save the workflow workflow = client.workflows.save(workflow_def) print(f"Created workflow: {workflow.id}") ``` ## Advanced Connection Patterns ### Linear Chains For simple sequential processing: ```python theme={null} # Create a linear chain of nodes nodes = [input_node, processor1, processor2, processor3, output_node] # Connect them in sequence for i in range(len(nodes) - 1): workflow_def.link(nodes[i].output(), nodes[i + 1].input()) # Or use the convenience method workflow_def.link_many(input_node, processor1, processor2, processor3, output_node) ``` ### Parallel Processing Process data through multiple paths simultaneously: ```python theme={null} # Split processing into parallel paths input_node = workflow_def.node("InputNode") # Path 1: Summarization summarizer = workflow_def.node("SummaryNode") workflow_def.link(input_node.output(), summarizer.input()) # Path 2: Sentiment analysis sentiment_analyzer = workflow_def.node("TextGenerationNode").config( template="Analyze the sentiment of: ((Input))" ) workflow_def.link(input_node.output(), sentiment_analyzer.input("variables", "Input")) # Path 3: Key phrase extraction key_phrases = workflow_def.node("TextGenerationNode").config( template="Extract key phrases from: ((Input))" ) workflow_def.link(input_node.output(), key_phrases.input("variables", "Input")) # Combine results combiner = workflow_def.node("ComposeTextNode").config( template="""Summary: ((Summary)) Sentiment: ((Sentiment)) Key Phrases: ((Key Phrases))""" ) workflow_def.link(summarizer.output(), combiner.input("variables", "Summary")) workflow_def.link(sentiment_analyzer.output(), combiner.input("variables", "Sentiment")) workflow_def.link(key_phrases.output(), combiner.input("variables", "Key Phrases")) ``` ### Conditional Logic Create branching logic based on conditions: ```python theme={null} # Input processing input_node = workflow_def.node("InputNode") # Condition check condition_node = workflow_def.node("ConditionalNode").config( condition="length > 1000", condition_type="text_length" ) # Path for long text long_text_processor = workflow_def.node("SummaryNode").config( summary_format="Paragraph", max_length=300 ) # Path for short text short_text_processor = workflow_def.node("TextGenerationNode").config( template="Expand on this topic: ((Input))" ) # Connect conditional logic workflow_def.link(input_node.output(), condition_node.input()) workflow_def.link(condition_node.output("true"), long_text_processor.input()) workflow_def.link(condition_node.output("false"), short_text_processor.input()) ``` ## Validation and Testing ### Validate Your Workflow Before saving, validate your workflow structure: ```python theme={null} # Check for common issues def validate_workflow(workflow_def): nodes = workflow_def.nodes # Check for orphaned nodes connected_nodes = set() for edge in workflow_def.edges: connected_nodes.add(edge.source_node_id) connected_nodes.add(edge.target_node_id) orphaned = [node for node in nodes if node.id not in connected_nodes] if orphaned: print(f"Warning: Orphaned nodes found: {[n.label for n in orphaned]}") # Check for missing required configurations for node in nodes: if node.type == "TextGenerationNode" and not node.config.get("template"): print(f"Warning: Node '{node.label}' missing template") return len(orphaned) == 0 # Validate before saving if validate_workflow(workflow_def): workflow = client.workflows.save(workflow_def) else: print("Please fix validation errors before saving") ``` ### Test with Sample Data Test your workflow with sample inputs: ```python theme={null} # Save and test the workflow workflow = client.workflows.save(workflow_def) # Test with sample data test_input = { "User Input": "What are the benefits of renewable energy?" } # Run the workflow run = workflow.run(body=test_input) result = run.wait(interval=2) print(f"Test result: {result.output}") print(f"Execution time: {result.execution_time}ms") ``` ## Best Practices Use descriptive labels for your nodes: ```python theme={null} # ❌ Bad - unclear purpose node1 = workflow_def.node("TextGenerationNode").config(label="Node 1") # ✅ Good - clear purpose question_answerer = workflow_def.node("TextGenerationNode").config( label="Question Answerer" ) ``` Create clear, well-structured templates: ```python theme={null} # ✅ Good template with clear structure template = """You are an expert analyst. Please analyze the following: Content: ((Input Content)) Focus Area: ((Analysis Focus)) Please provide: 1. Key insights 2. Recommendations 3. Next steps Format your response clearly with headers.""" ``` Plan for potential failures: `python # Add validation nodes validator = workflow_def.node("ConditionalNode").config( condition="not_empty", condition_type="text_validation" ) # Add fallback paths error_handler = workflow_def.node("TextGenerationNode").config( template="Unable to process input. Please provide valid text content." ) workflow_def.link(validator.output("false"), error_handler.input()) ` Optimize for efficiency: ```python theme={null} # Use appropriate model sizes quick_task = workflow_def.node("TextGenerationNode").config( model=["gpt-4o-mini"], # Faster for simple tasks max_tokens=100 ) complex_task = workflow_def.node("TextGenerationNode").config( model=["gpt-4o"], # More capable for complex tasks max_tokens=500 ) ``` ## Troubleshooting Common Issues **Problem**: Nodes won't connect or connections fail **Solutions**: * Check that output and input types are compatible * Verify node labels and variable names are correct * Ensure required node configurations are set ```python theme={null} # Debug connection issues print(f"Source node outputs: {source_node.outputs}") print(f"Target node inputs: {target_node.inputs}") ``` **Problem**: Template variables not being replaced **Solutions**: * Use exact variable names: `((Variable Name))` * Check that input connections use the correct variable key * Verify node labels match template variables ```python theme={null} # Correct variable usage template = "Process this: ((User Input))" workflow_def.link(input_node.output(), ai_node.input("variables", "User Input")) ``` **Problem**: Nodes not behaving as expected **Solutions**: * Check required configuration parameters * Verify model names and settings * Test with minimal configurations first ```python theme={null} # Get available node configuration options nodes = client.get_nodes() text_gen_node = next(n for n in nodes if n["type"] == "TextGenerationNode") print(f"Available config: {text_gen_node['config_schema']}") ``` ## Next Steps Learn how to execute workflows and handle results Explore all available node types and their configurations See complete workflow examples for common use cases Learn advanced workflow design patterns and techniques ``` ``` # Workflow Examples Source: https://docs.noxus.ai/sdk/workflows/examples Real-world workflow examples for common use cases and patterns ## Overview This page provides complete, ready-to-use workflow examples for common scenarios. Each example includes the full code, explanation, and variations you can adapt for your needs. ## Content Generation Workflows ### Blog Post Generator Create comprehensive blog posts with research and fact-checking. ```python theme={null} from noxus_sdk.client import Client from noxus_sdk.workflows import WorkflowDefinition client = Client(api_key="your_api_key_here") # Create blog post generation workflow blog_workflow = WorkflowDefinition(name="Blog Post Generator") # Input nodes topic_input = blog_workflow.node("InputNode").config( label="Blog Topic", type="str" ) audience_input = blog_workflow.node("InputNode").config( label="Target Audience", type="str", fixed_value=True, value="general audience" ) # Research phase research_node = blog_workflow.node("TextGenerationNode").config( template="""Research the topic "((Blog Topic))" and provide: 1. Key facts and statistics 2. Current trends and developments 3. Common questions people have 4. Expert opinions or quotes Topic: ((Blog Topic)) Target Audience: ((Target Audience))""", model=["gpt-4o-mini"], temperature=0.3, max_tokens=800 ) # Outline creation outline_node = blog_workflow.node("TextGenerationNode").config( template="""Based on this research, create a detailed blog post outline: Research: ((Research)) Create an outline with: - Compelling headline - Introduction hook - 3-5 main sections with subpoints - Conclusion with call-to-action Target audience: ((Target Audience))""", model=["gpt-4o-mini"], temperature=0.5, max_tokens=400 ) # Content generation content_node = blog_workflow.node("TextGenerationNode").config( template="""Write a complete blog post based on this outline and research: Outline: ((Outline)) Research: ((Research)) Requirements: - Engaging and informative tone - Include relevant examples - Use subheadings for readability - 800-1200 words - Target audience: ((Target Audience))""", model=["gpt-4o"], temperature=0.7, max_tokens=1500 ) # SEO optimization seo_node = blog_workflow.node("TextGenerationNode").config( template="""Optimize this blog post for SEO: Blog Post: ((Blog Post)) Add: - Meta description (150-160 characters) - 5-7 relevant keywords - Suggested internal/external links - Social media snippet""", model=["gpt-4o-mini"], temperature=0.3, max_tokens=300 ) # Final composition final_post = blog_workflow.node("ComposeTextNode").config( template="""# Complete Blog Post Package ## Blog Post ((Blog Post)) ## SEO Optimization ((SEO Details)) --- Generated on: {{current_date}} """) output_node = blog_workflow.node("OutputNode") # Connect the workflow blog_workflow.link(topic_input.output(), research_node.input("variables", "Blog Topic")) blog_workflow.link(audience_input.output(), research_node.input("variables", "Target Audience")) blog_workflow.link(audience_input.output(), outline_node.input("variables", "Target Audience")) blog_workflow.link(audience_input.output(), content_node.input("variables", "Target Audience")) blog_workflow.link(research_node.output(), outline_node.input("variables", "Research")) blog_workflow.link(research_node.output(), content_node.input("variables", "Research")) blog_workflow.link(outline_node.output(), content_node.input("variables", "Outline")) blog_workflow.link(content_node.output(), seo_node.input("variables", "Blog Post")) blog_workflow.link(content_node.output(), final_post.input("variables", "Blog Post")) blog_workflow.link(seo_node.output(), final_post.input("variables", "SEO Details")) blog_workflow.link(final_post.output(), output_node.input()) # Save and test blog_generator = client.workflows.save(blog_workflow) # Run with sample input result = blog_generator.run(body={ "Blog Topic": "The Future of Remote Work in 2024" }).wait() print(result.output) ``` ### Social Media Content Creator Generate coordinated content across multiple social platforms. ```python theme={null} social_workflow = WorkflowDefinition(name="Social Media Content Creator") # Inputs topic_input = social_workflow.node("InputNode").config( label="Content Topic", type="str" ) brand_voice = social_workflow.node("InputNode").config( label="Brand Voice", type="str", fixed_value=True, value="friendly, professional, engaging" ) # Core message creation core_message = social_workflow.node("TextGenerationNode").config( template="""Create a core message about "((Content Topic))" that: - Captures the main value proposition - Is engaging and shareable - Reflects this brand voice: ((Brand Voice)) - Can be adapted for different platforms Keep it concise but impactful.""", model=["gpt-4o-mini"], temperature=0.7, max_tokens=200 ) # Platform-specific adaptations twitter_post = social_workflow.node("TextGenerationNode").config( template="""Adapt this core message for Twitter: Core Message: ((Core Message)) Requirements: - Under 280 characters - Include 2-3 relevant hashtags - Engaging and conversation-starting - Brand voice: ((Brand Voice))""", model=["gpt-4o-mini"], temperature=0.6, max_tokens=100 ) linkedin_post = social_workflow.node("TextGenerationNode").config( template="""Adapt this core message for LinkedIn: Core Message: ((Core Message)) Requirements: - Professional tone - 1-3 paragraphs - Include a thought-provoking question - Suitable for business audience - Brand voice: ((Brand Voice))""", model=["gpt-4o-mini"], temperature=0.5, max_tokens=300 ) instagram_post = social_workflow.node("TextGenerationNode").config( template="""Adapt this core message for Instagram: Core Message: ((Core Message)) Requirements: - Visual and engaging - Include emoji where appropriate - 5-10 relevant hashtags - Call-to-action in comments - Brand voice: ((Brand Voice))""", model=["gpt-4o-mini"], temperature=0.7, max_tokens=200 ) # Combine all content social_package = social_workflow.node("ComposeTextNode").config( template="""# Social Media Content Package ## Core Message ((Core Message)) ## Twitter ((Twitter Post)) ## LinkedIn ((LinkedIn Post)) ## Instagram ((Instagram Post)) --- Created: {{current_date}} Topic: ((Content Topic)) """) output_node = social_workflow.node("OutputNode") # Connect workflow social_workflow.link(topic_input.output(), core_message.input("variables", "Content Topic")) social_workflow.link(brand_voice.output(), core_message.input("variables", "Brand Voice")) social_workflow.link(core_message.output(), twitter_post.input("variables", "Core Message")) social_workflow.link(core_message.output(), linkedin_post.input("variables", "Core Message")) social_workflow.link(core_message.output(), instagram_post.input("variables", "Core Message")) social_workflow.link(brand_voice.output(), twitter_post.input("variables", "Brand Voice")) social_workflow.link(brand_voice.output(), linkedin_post.input("variables", "Brand Voice")) social_workflow.link(brand_voice.output(), instagram_post.input("variables", "Brand Voice")) social_workflow.link(topic_input.output(), social_package.input("variables", "Content Topic")) social_workflow.link(core_message.output(), social_package.input("variables", "Core Message")) social_workflow.link(twitter_post.output(), social_package.input("variables", "Twitter Post")) social_workflow.link(linkedin_post.output(), social_package.input("variables", "LinkedIn Post")) social_workflow.link(instagram_post.output(), social_package.input("variables", "Instagram Post")) social_workflow.link(social_package.output(), output_node.input()) # Save workflow social_creator = client.workflows.save(social_workflow) ``` ## Document Processing Workflows ### Contract Analysis Workflow Analyze legal contracts and extract key information. ```python theme={null} contract_workflow = WorkflowDefinition(name="Contract Analyzer") # File input contract_input = contract_workflow.node("FileInputNode").config( label="Contract Document", accepted_types=["pdf", "docx", "txt"], max_size_mb=20 ) # Extract text text_extractor = contract_workflow.node("ExtractTextNode").config( preserve_formatting=True, extract_tables=True ) # Key terms extraction key_terms = contract_workflow.node("TextGenerationNode").config( template="""Analyze this contract and extract key terms: Contract Text: ((Contract Text)) Extract and format: 1. Parties involved 2. Contract duration/dates 3. Payment terms 4. Key obligations for each party 5. Termination clauses 6. Liability limitations 7. Governing law Present in a structured format.""", model=["gpt-4o"], temperature=0.1, max_tokens=800 ) # Risk assessment risk_analysis = contract_workflow.node("TextGenerationNode").config( template="""Perform a risk assessment of this contract: Contract Text: ((Contract Text)) Key Terms: ((Key Terms)) Identify: 1. High-risk clauses 2. Missing standard protections 3. Unusual or concerning terms 4. Recommendations for negotiation 5. Overall risk level (Low/Medium/High) Provide specific examples and explanations.""", model=["gpt-4o"], temperature=0.2, max_tokens=600 ) # Summary generation contract_summary = contract_workflow.node("SummaryNode").config( summary_format="Key Points", summary_topic="Contract overview and main provisions", max_length=300 ) # Compliance check compliance_check = contract_workflow.node("TextGenerationNode").config( template="""Check this contract for common compliance issues: Contract Text: ((Contract Text)) Review for: 1. Required legal disclosures 2. Industry-specific regulations 3. Data protection compliance (GDPR, etc.) 4. Employment law compliance (if applicable) 5. Consumer protection requirements Flag any potential compliance issues.""", model=["gpt-4o"], temperature=0.1, max_tokens=400 ) # Final report final_report = contract_workflow.node("ComposeTextNode").config( template="""# Contract Analysis Report ## Executive Summary ((Contract Summary)) ## Key Terms & Provisions ((Key Terms)) ## Risk Assessment ((Risk Analysis)) ## Compliance Review ((Compliance Check)) --- Analysis completed: {{current_date}} Report ID: {{uuid}} **Disclaimer**: This analysis is for informational purposes only and does not constitute legal advice. """) output_node = contract_workflow.node("OutputNode") # Connect workflow contract_workflow.link(contract_input.output(), text_extractor.input()) contract_workflow.link(text_extractor.output(), key_terms.input("variables", "Contract Text")) contract_workflow.link(text_extractor.output(), risk_analysis.input("variables", "Contract Text")) contract_workflow.link(text_extractor.output(), contract_summary.input()) contract_workflow.link(text_extractor.output(), compliance_check.input("variables", "Contract Text")) contract_workflow.link(key_terms.output(), risk_analysis.input("variables", "Key Terms")) contract_workflow.link(contract_summary.output(), final_report.input("variables", "Contract Summary")) contract_workflow.link(key_terms.output(), final_report.input("variables", "Key Terms")) contract_workflow.link(risk_analysis.output(), final_report.input("variables", "Risk Analysis")) contract_workflow.link(compliance_check.output(), final_report.input("variables", "Compliance Check")) contract_workflow.link(final_report.output(), output_node.input()) # Save workflow contract_analyzer = client.workflows.save(contract_workflow) ``` ### Research Paper Processor Process academic papers and generate insights. ```python theme={null} research_workflow = WorkflowDefinition(name="Research Paper Processor") # Inputs paper_input = research_workflow.node("FileInputNode").config( label="Research Paper", accepted_types=["pdf"], max_size_mb=50 ) research_focus = research_workflow.node("InputNode").config( label="Research Focus", type="str", fixed_value=True, value="methodology, findings, and implications" ) # Extract text text_extractor = research_workflow.node("ExtractTextNode").config( preserve_formatting=True, extract_tables=True, extract_metadata=True ) # Structure identification structure_analyzer = research_workflow.node("TextGenerationNode").config( template="""Analyze the structure of this research paper: Paper Text: ((Paper Text)) Identify and extract: 1. Title and authors 2. Abstract 3. Introduction/background 4. Methodology 5. Results/findings 6. Discussion 7. Conclusion 8. References (key ones) Format each section clearly.""", model=["gpt-4o"], temperature=0.1, max_tokens=1000 ) # Methodology analysis methodology_analysis = research_workflow.node("TextGenerationNode").config( template="""Analyze the methodology of this research: Paper Structure: ((Paper Structure)) Full Text: ((Paper Text)) Focus on: 1. Research design and approach 2. Data collection methods 3. Sample size and characteristics 4. Analysis techniques used 5. Limitations acknowledged 6. Validity and reliability considerations Provide a critical assessment.""", model=["gpt-4o"], temperature=0.2, max_tokens=600 ) # Key findings extraction findings_extractor = research_workflow.node("TextGenerationNode").config( template="""Extract and summarize the key findings: Paper Structure: ((Paper Structure)) Full Text: ((Paper Text)) Identify: 1. Main research questions answered 2. Primary findings and results 3. Statistical significance (if applicable) 4. Unexpected or surprising results 5. Practical implications 6. Theoretical contributions Present findings clearly and objectively.""", model=["gpt-4o"], temperature=0.1, max_tokens=700 ) # Critical evaluation critical_evaluation = research_workflow.node("TextGenerationNode").config( template="""Provide a critical evaluation of this research: Methodology: ((Methodology Analysis)) Findings: ((Key Findings)) Full Paper: ((Paper Text)) Evaluate: 1. Strengths of the research 2. Potential weaknesses or limitations 3. Quality of evidence presented 4. Generalizability of findings 5. Contribution to the field 6. Suggestions for future research Be balanced and constructive.""", model=["gpt-4o"], temperature=0.3, max_tokens=800 ) # Executive summary executive_summary = research_workflow.node("SummaryNode").config( summary_format="Key Points", summary_topic="Research paper overview for non-experts", max_length=400 ) # Final report research_report = research_workflow.node("ComposeTextNode").config( template="""# Research Paper Analysis Report ## Executive Summary ((Executive Summary)) ## Paper Structure & Content ((Paper Structure)) ## Methodology Assessment ((Methodology Analysis)) ## Key Findings ((Key Findings)) ## Critical Evaluation ((Critical Evaluation)) --- Analysis completed: {{current_date}} Focus area: ((Research Focus)) """) output_node = research_workflow.node("OutputNode") # Connect workflow research_workflow.link(paper_input.output(), text_extractor.input()) research_workflow.link(text_extractor.output(), structure_analyzer.input("variables", "Paper Text")) research_workflow.link(text_extractor.output(), executive_summary.input()) research_workflow.link(structure_analyzer.output(), methodology_analysis.input("variables", "Paper Structure")) research_workflow.link(text_extractor.output(), methodology_analysis.input("variables", "Paper Text")) research_workflow.link(structure_analyzer.output(), findings_extractor.input("variables", "Paper Structure")) research_workflow.link(text_extractor.output(), findings_extractor.input("variables", "Paper Text")) research_workflow.link(methodology_analysis.output(), critical_evaluation.input("variables", "Methodology Analysis")) research_workflow.link(findings_extractor.output(), critical_evaluation.input("variables", "Key Findings")) research_workflow.link(text_extractor.output(), critical_evaluation.input("variables", "Paper Text")) research_workflow.link(research_focus.output(), research_report.input("variables", "Research Focus")) research_workflow.link(executive_summary.output(), research_report.input("variables", "Executive Summary")) research_workflow.link(structure_analyzer.output(), research_report.input("variables", "Paper Structure")) research_workflow.link(methodology_analysis.output(), research_report.input("variables", "Methodology Analysis")) research_workflow.link(findings_extractor.output(), research_report.input("variables", "Key Findings")) research_workflow.link(critical_evaluation.output(), research_report.input("variables", "Critical Evaluation")) research_workflow.link(research_report.output(), output_node.input()) # Save workflow research_processor = client.workflows.save(research_workflow) ``` ## Customer Support Workflows ### Ticket Classification and Response Automatically classify support tickets and generate initial responses. ```python theme={null} support_workflow = WorkflowDefinition(name="Support Ticket Processor") # Inputs ticket_input = support_workflow.node("InputNode").config( label="Support Ticket", type="str" ) customer_tier = support_workflow.node("InputNode").config( label="Customer Tier", type="str", fixed_value=True, value="standard" ) # Ticket classification classifier = support_workflow.node("TextGenerationNode").config( template="""Classify this support ticket: Ticket: ((Support Ticket)) Classify by: 1. Category (Technical, Billing, Account, Feature Request, Bug Report, Other) 2. Priority (Low, Medium, High, Critical) 3. Urgency (Low, Medium, High) 4. Complexity (Simple, Moderate, Complex) 5. Department (Support, Engineering, Sales, Billing) Provide reasoning for each classification.""", model=["gpt-4o-mini"], temperature=0.1, max_tokens=300 ) # Sentiment analysis sentiment_analyzer = support_workflow.node("TextGenerationNode").config( template="""Analyze the sentiment and tone of this support ticket: Ticket: ((Support Ticket)) Determine: 1. Overall sentiment (Positive, Neutral, Negative, Very Negative) 2. Emotional indicators (frustrated, confused, angry, satisfied, etc.) 3. Urgency level from customer perspective 4. Communication style needed in response Provide specific examples from the text.""", model=["gpt-4o-mini"], temperature=0.2, max_tokens=200 ) # Knowledge base search (simulated) kb_search = support_workflow.node("TextGenerationNode").config( template="""Based on this ticket classification, suggest relevant knowledge base articles: Ticket: ((Support Ticket)) Classification: ((Classification)) Suggest: 1. Most relevant help articles (by title) 2. Common solutions for this type of issue 3. Troubleshooting steps 4. Related documentation Format as a helpful resource list.""", model=["gpt-4o-mini"], temperature=0.3, max_tokens=400 ) # Response generation response_generator = support_workflow.node("TextGenerationNode").config( template="""Generate a professional support response: Ticket: ((Support Ticket)) Classification: ((Classification)) Sentiment Analysis: ((Sentiment Analysis)) Suggested Resources: ((KB Search)) Customer Tier: ((Customer Tier)) Create a response that: 1. Acknowledges the customer's issue 2. Shows empathy if needed 3. Provides initial troubleshooting steps 4. References helpful resources 5. Sets expectations for follow-up 6. Matches the appropriate tone Keep it professional but personalized.""", model=["gpt-4o"], temperature=0.4, max_tokens=500 ) # Escalation check escalation_check = support_workflow.node("ConditionalNode").config( condition="priority == 'Critical' or priority == 'High'", condition_type="custom" ) # Escalation notice escalation_notice = support_workflow.node("TextGenerationNode").config( template="""Generate escalation notice: Ticket: ((Support Ticket)) Classification: ((Classification)) Sentiment: ((Sentiment Analysis)) Create internal escalation notice including: 1. Reason for escalation 2. Customer impact 3. Recommended next steps 4. Timeline requirements""", model=["gpt-4o-mini"], temperature=0.1, max_tokens=200 ) # Final package support_package = support_workflow.node("ComposeTextNode").config( template="""# Support Ticket Analysis ## Ticket Classification ((Classification)) ## Sentiment Analysis ((Sentiment Analysis)) ## Suggested Response ((Response)) ## Recommended Resources ((KB Search)) ## Escalation Status ((Escalation Notice)) --- Processed: {{current_date}} Customer Tier: ((Customer Tier)) """) output_node = support_workflow.node("OutputNode") # Connect workflow support_workflow.link(ticket_input.output(), classifier.input("variables", "Support Ticket")) support_workflow.link(ticket_input.output(), sentiment_analyzer.input("variables", "Support Ticket")) support_workflow.link(ticket_input.output(), kb_search.input("variables", "Support Ticket")) support_workflow.link(classifier.output(), kb_search.input("variables", "Classification")) support_workflow.link(ticket_input.output(), response_generator.input("variables", "Support Ticket")) support_workflow.link(classifier.output(), response_generator.input("variables", "Classification")) support_workflow.link(sentiment_analyzer.output(), response_generator.input("variables", "Sentiment Analysis")) support_workflow.link(kb_search.output(), response_generator.input("variables", "KB Search")) support_workflow.link(customer_tier.output(), response_generator.input("variables", "Customer Tier")) # Escalation logic support_workflow.link(classifier.output(), escalation_check.input()) support_workflow.link(escalation_check.output("true"), escalation_notice.input("variables", "Classification")) support_workflow.link(ticket_input.output(), escalation_notice.input("variables", "Support Ticket")) support_workflow.link(sentiment_analyzer.output(), escalation_notice.input("variables", "Sentiment Analysis")) # Final assembly support_workflow.link(classifier.output(), support_package.input("variables", "Classification")) support_workflow.link(sentiment_analyzer.output(), support_package.input("variables", "Sentiment Analysis")) support_workflow.link(response_generator.output(), support_package.input("variables", "Response")) support_workflow.link(kb_search.output(), support_package.input("variables", "KB Search")) support_workflow.link(escalation_notice.output(), support_package.input("variables", "Escalation Notice")) support_workflow.link(customer_tier.output(), support_package.input("variables", "Customer Tier")) support_workflow.link(support_package.output(), output_node.input()) # Save workflow support_processor = client.workflows.save(support_workflow) ``` ## Data Analysis Workflows ### Survey Response Analyzer Analyze survey responses and generate insights. ```python theme={null} survey_workflow = WorkflowDefinition(name="Survey Response Analyzer") # Input survey_data = survey_workflow.node("InputNode").config( label="Survey Responses", type="str", description="Paste survey responses (CSV format or structured text)" ) survey_topic = survey_workflow.node("InputNode").config( label="Survey Topic", type="str" ) # Data preprocessing data_processor = survey_workflow.node("TextGenerationNode").config( template="""Process and structure this survey data: Raw Data: ((Survey Responses)) Topic: ((Survey Topic)) Tasks: 1. Identify response patterns 2. Count total responses 3. Categorize response types 4. Flag incomplete or invalid responses 5. Prepare data for analysis Present in a structured format.""", model=["gpt-4o-mini"], temperature=0.1, max_tokens=600 ) # Sentiment analysis sentiment_analysis = survey_workflow.node("TextGenerationNode").config( template="""Analyze sentiment in these survey responses: Processed Data: ((Processed Data)) Provide: 1. Overall sentiment distribution (% positive, neutral, negative) 2. Key positive themes 3. Key negative themes 4. Sentiment by question/topic (if applicable) 5. Notable emotional indicators""", model=["gpt-4o-mini"], temperature=0.2, max_tokens=500 ) # Theme extraction theme_extractor = survey_workflow.node("TextGenerationNode").config( template="""Extract key themes from survey responses: Processed Data: ((Processed Data)) Survey Topic: ((Survey Topic)) Identify: 1. Most frequently mentioned topics 2. Emerging themes or patterns 3. Unexpected insights 4. Common suggestions or requests 5. Areas of consensus vs. disagreement Group similar responses and quantify when possible.""", model=["gpt-4o"], temperature=0.3, max_tokens=700 ) # Statistical summary stats_generator = survey_workflow.node("TextGenerationNode").config( template="""Generate statistical summary: Processed Data: ((Processed Data)) Themes: ((Themes)) Sentiment: ((Sentiment Analysis)) Create summary including: 1. Response rate and demographics (if available) 2. Key metrics and percentages 3. Statistical significance of findings 4. Confidence levels 5. Data quality assessment""", model=["gpt-4o-mini"], temperature=0.1, max_tokens=400 ) # Recommendations recommendations = survey_workflow.node("TextGenerationNode").config( template="""Based on the survey analysis, provide actionable recommendations: Themes: ((Themes)) Sentiment: ((Sentiment Analysis)) Statistics: ((Statistics)) Survey Topic: ((Survey Topic)) Recommend: 1. Priority actions based on feedback 2. Areas requiring immediate attention 3. Long-term strategic considerations 4. Follow-up survey questions 5. Implementation timeline suggestions Focus on practical, data-driven recommendations.""", model=["gpt-4o"], temperature=0.4, max_tokens=600 ) # Executive summary exec_summary = survey_workflow.node("SummaryNode").config( summary_format="Key Points", summary_topic="Survey results and main insights", max_length=300 ) # Final report survey_report = survey_workflow.node("ComposeTextNode").config( template="""# Survey Analysis Report: ((Survey Topic)) ## Executive Summary ((Executive Summary)) ## Response Overview ((Statistics)) ## Sentiment Analysis ((Sentiment Analysis)) ## Key Themes & Insights ((Themes)) ## Recommendations ((Recommendations)) --- Analysis Date: {{current_date}} Report ID: {{uuid}} """) output_node = survey_workflow.node("OutputNode") # Connect workflow survey_workflow.link(survey_data.output(), data_processor.input("variables", "Survey Responses")) survey_workflow.link(survey_topic.output(), data_processor.input("variables", "Survey Topic")) survey_workflow.link(data_processor.output(), sentiment_analysis.input("variables", "Processed Data")) survey_workflow.link(data_processor.output(), theme_extractor.input("variables", "Processed Data")) survey_workflow.link(survey_topic.output(), theme_extractor.input("variables", "Survey Topic")) survey_workflow.link(data_processor.output(), stats_generator.input("variables", "Processed Data")) survey_workflow.link(theme_extractor.output(), stats_generator.input("variables", "Themes")) survey_workflow.link(sentiment_analysis.output(), stats_generator.input("variables", "Sentiment Analysis")) survey_workflow.link(theme_extractor.output(), recommendations.input("variables", "Themes")) survey_workflow.link(sentiment_analysis.output(), recommendations.input("variables", "Sentiment Analysis")) survey_workflow.link(stats_generator.output(), recommendations.input("variables", "Statistics")) survey_workflow.link(survey_topic.output(), recommendations.input("variables", "Survey Topic")) survey_workflow.link(data_processor.output(), exec_summary.input()) survey_workflow.link(survey_topic.output(), survey_report.input("variables", "Survey Topic")) survey_workflow.link(exec_summary.output(), survey_report.input("variables", "Executive Summary")) survey_workflow.link(stats_generator.output(), survey_report.input("variables", "Statistics")) survey_workflow.link(sentiment_analysis.output(), survey_report.input("variables", "Sentiment Analysis")) survey_workflow.link(theme_extractor.output(), survey_report.input("variables", "Themes")) survey_workflow.link(recommendations.output(), survey_report.input("variables", "Recommendations")) survey_workflow.link(survey_report.output(), output_node.input()) # Save workflow survey_analyzer = client.workflows.save(survey_workflow) ``` ## Running the Examples ### Test the Blog Post Generator ```python theme={null} # Run the blog post generator blog_result = blog_generator.run(body={ "Blog Topic": "The Impact of AI on Small Businesses" }).wait() print("Generated Blog Post:") print(blog_result.output) ``` ### Test the Contract Analyzer ```python theme={null} # Test with a sample contract (you'd upload a real file) contract_result = contract_analyzer.run(body={ # File would be uploaded through the UI or API }).wait() print("Contract Analysis:") print(contract_result.output) ``` ### Test the Support Ticket Processor ```python theme={null} # Test support ticket processing support_result = support_processor.run(body={ "Support Ticket": """ Hi, I'm really frustrated. I've been trying to log into my account for 3 days and keep getting an error message saying 'invalid credentials' even though I'm sure my password is correct. I've tried resetting it twice but still can't get in. This is blocking me from accessing important files for a client presentation tomorrow. Please help ASAP! """, "Customer Tier": "premium" }).wait() print("Support Analysis:") print(support_result.output) ``` ## Workflow Patterns and Best Practices Add validation and error handling to your workflows: ```python theme={null} # Add input validation validator = workflow_def.node("ValidationNode").config( validation_type="text_length", min_length=10, on_validation_error="default_value", default_value="Please provide more detailed input." ) # Add conditional error paths error_handler = workflow_def.node("ConditionalNode").config( condition="contains_error", condition_type="custom" ) ``` Process multiple aspects simultaneously: ```python theme={null} # Split input to multiple processors workflow_def.link(input_node.output(), processor1.input()) workflow_def.link(input_node.output(), processor2.input()) workflow_def.link(input_node.output(), processor3.input()) # Merge results merger = workflow_def.node("MergeNode").config( merge_strategy="combine", wait_for_all=True ) ``` Add quality checks and refinement: ```python theme={null} # Initial generation generator = workflow_def.node("TextGenerationNode") # Quality check quality_check = workflow_def.node("TextGenerationNode").config( template="Review this content for quality and suggest improvements: ((Content))" ) # Refinement refiner = workflow_def.node("TextGenerationNode").config( template="Improve this content based on feedback: ((Content)) Feedback: ((Feedback))" ) ``` ## Next Steps Learn the fundamentals of workflow construction Execute workflows and handle results effectively Explore all available node types and configurations Master advanced workflow design patterns # Node Types Source: https://docs.noxus.ai/sdk/workflows/node-types Complete reference for all available workflow node types and their configurations ## Overview Noxus workflows are built using various node types, each designed for specific tasks. This reference covers all available node types, their configurations, and usage examples. You can get the most up-to-date list of available node types by calling `client.get_nodes()` in your code. ## Input/Output Nodes ### InputNode The basic input node for accepting data into your workflow. ```python theme={null} input_node = workflow_def.node("InputNode").config( label="User Input", # Display name for the input type="str", # Data type: "str", "int", "float", "bool" fixed_value=False, # Whether value is set at design time value="default value", # Default/fixed value (if fixed_value=True) required=True, # Whether input is required description="Enter your text" # Help text for users ) ``` Display name for the input field Data type: `str`, `int`, `float`, `bool`, `list`, `dict` If true, uses the `value` parameter instead of runtime input Default value or fixed value (when `fixed_value=true`) Whether this input is required for workflow execution ### FileInputNode Accepts file uploads as workflow input. ```python theme={null} file_input = workflow_def.node("FileInputNode").config( label="Document Upload", accepted_types=["pdf", "txt", "docx", "xlsx"], max_size_mb=10, multiple=False, extract_text=True ) ``` List of allowed file extensions Maximum file size in megabytes Whether to accept multiple files Automatically extract text content from supported file types ### OutputNode Defines the final output of your workflow. ```python theme={null} output_node = workflow_def.node("OutputNode").config( label="Final Result", format="text", # "text", "json", "file" include_metadata=False # Include execution metadata ) ``` ## AI & Language Model Nodes ### TextGenerationNode Generate text using various AI models. ```python theme={null} text_gen = workflow_def.node("TextGenerationNode").config( template="Answer this question: ((Question))\n\nContext: ((Context))", model=["gpt-4o-mini"], temperature=0.7, max_tokens=500, top_p=0.9, frequency_penalty=0.0, presence_penalty=0.0, stop_sequences=["END", "STOP"], system_prompt="You are a helpful assistant." ) ``` Text template with variable placeholders in format `((Variable Name))` List of model names to use (first available will be selected) Creativity level (0.0 = deterministic, 1.0 = very creative) Maximum number of tokens to generate Nucleus sampling parameter System-level instructions for the model ### SummaryNode Create summaries of text content. ```python theme={null} summarizer = workflow_def.node("SummaryNode").config( summary_format="Bullet Points", # "Paragraph", "Bullet Points", "Key Points" summary_topic="Main insights", # Focus area for summarization max_length=300, # Maximum summary length in words language="English", # Output language include_quotes=False, # Include relevant quotes extraction_mode="comprehensive" # "comprehensive", "key_points", "abstract" ) ``` Format of the summary: `Paragraph`, `Bullet Points`, `Key Points` Specific focus area or topic for the summary Maximum length in words Output language for the summary ### TranslationNode Translate text between languages. ```python theme={null} translator = workflow_def.node("TranslationNode").config( target_language="Spanish", source_language="auto", # "auto" for auto-detection preserve_formatting=True, translation_style="formal", # "formal", "casual", "technical" include_original=False ) ``` Target language for translation Source language or "auto" for automatic detection Maintain original text formatting Translation style: `formal`, `casual`, `technical` ### EmbeddingNode Generate vector embeddings for text. ```python theme={null} embedding = workflow_def.node("EmbeddingNode").config( model="text-embedding-ada-002", chunk_size=1000, chunk_overlap=200, normalize=True ) ``` ## Data Processing Nodes ### ComposeTextNode Combine multiple text inputs using templates. ```python theme={null} composer = workflow_def.node("ComposeTextNode").config( template="""# Report: ((Title)) ## Summary ((Summary)) ## Analysis ((Analysis)) ## Recommendations ((Recommendations)) --- Generated on: {{current_date}} Report ID: {{uuid}} """, output_format="markdown", # "text", "markdown", "html" include_metadata=True ) ``` Template with variable placeholders `((Variable))` and system variables ` {{ system_var }}` Output format: `text`, `markdown`, `html` **Available System Variables:** * `{{current_date}}` - Current date * `{{current_time}}` - Current time * `{{uuid}}` - Unique identifier * `{{workflow_id}}` - Current workflow ID ### ExtractTextNode Extract text content from various file formats. ```python theme={null} extractor = workflow_def.node("ExtractTextNode").config( preserve_formatting=True, extract_tables=True, extract_images=False, extract_metadata=True, output_format="plain_text", # "plain_text", "markdown", "structured" language="auto" # Language hint for OCR ) ``` Maintain original document formatting Extract and format table data Extract image descriptions (requires vision models) ### FilterNode Filter data based on conditions. ```python theme={null} filter_node = workflow_def.node("FilterNode").config( condition="length > 100", # Filter condition filter_type="text_length", # "text_length", "contains", "regex", "custom" case_sensitive=False, # For text-based filters regex_pattern=r"\b\w+@\w+\.\w+\b", # For regex filters custom_function="def filter_func(text): return len(text.split()) > 50" ) ``` Filter condition expression Type of filter: `text_length`, `contains`, `regex`, `custom` ### DataTransformNode Transform and manipulate data. ```python theme={null} transformer = workflow_def.node("DataTransformNode").config( transformation_type="json_to_text", # "json_to_text", "csv_to_json", "custom" custom_transform=""" def transform(data): # Custom transformation logic return processed_data """, output_schema={ # Expected output schema "type": "object", "properties": { "result": {"type": "string"} } } ) ``` ## Logic & Control Flow Nodes ### ConditionalNode Branch workflow execution based on conditions. ```python theme={null} conditional = workflow_def.node("ConditionalNode").config( condition="length > 1000", condition_type="text_length", # "text_length", "contains", "equals", "custom" comparison_value="1000", case_sensitive=False, custom_condition="def check(data): return len(data.split()) > 100" ) ``` The conditional node has two outputs: * `true` - Data that meets the condition * `false` - Data that doesn't meet the condition ```python theme={null} # Connect both paths workflow_def.link(conditional.output("true"), long_text_processor.input()) workflow_def.link(conditional.output("false"), short_text_processor.input()) ``` ### LoopNode Iterate over collections of data. ```python theme={null} loop_node = workflow_def.node("LoopNode").config( iteration_type="list", # "list", "range", "while" max_iterations=100, # Safety limit parallel_execution=False, # Process items in parallel batch_size=10, # Items per batch (if parallel) break_condition="error_count > 5" # Early termination condition ) ``` ### SwitchNode Route data based on values. ```python theme={null} switch = workflow_def.node("SwitchNode").config( switch_field="category", # Field to switch on cases={ "urgent": "urgent_processor", "normal": "normal_processor", "low": "low_priority_processor" }, default_case="normal_processor" # Default route ) ``` ### MergeNode Combine multiple data streams. ```python theme={null} merger = workflow_def.node("MergeNode").config( merge_strategy="concatenate", # "concatenate", "combine", "latest" separator="\n\n---\n\n", # For concatenation wait_for_all=True, # Wait for all inputs timeout_seconds=300 # Timeout for waiting ) ``` ## External Integration Nodes ### APICallNode Make HTTP requests to external services. ```python theme={null} api_call = workflow_def.node("APICallNode").config( url="https://api.example.com/endpoint", method="POST", # "GET", "POST", "PUT", "DELETE" headers={ "Authorization": "Bearer {{api_key}}", "Content-Type": "application/json" }, body_template='{"query": "((Query))", "options": {"format": "json"}}', timeout_seconds=30, retry_attempts=3, retry_delay=1 ) ``` API endpoint URL (can include variables) HTTP method HTTP headers (can include variables) Request body template with variables ### DatabaseQueryNode Query databases with SQL. ```python theme={null} db_query = workflow_def.node("DatabaseQueryNode").config( connection_string="postgresql://user:pass@localhost/db", query_template="SELECT * FROM users WHERE name LIKE '%((Name))%'", query_type="SELECT", # "SELECT", "INSERT", "UPDATE", "DELETE" max_rows=1000, timeout_seconds=30 ) ``` ### WebScraperNode Extract data from web pages. ```python theme={null} scraper = workflow_def.node("WebScraperNode").config( url_template="https://example.com/search?q=((Query))", selectors={ "title": "h1.title", "content": ".content p", "links": "a[href]" }, wait_for_element=".content", # Wait for element to load timeout_seconds=30, user_agent="Mozilla/5.0 (compatible; NoxusBot/1.0)" ) ``` ### EmailNode Send emails and notifications. ```python theme={null} email_node = workflow_def.node("EmailNode").config( smtp_server="smtp.gmail.com", smtp_port=587, username="your-email@gmail.com", password="{{email_password}}", # Use secure variable to_addresses=["recipient@example.com"], subject_template="Workflow Result: ((Subject))", body_template=""" Hello, Your workflow has completed with the following result: ((Result)) Best regards, Noxus Automation """, html_format=False ) ``` ## Specialized Nodes ### KnowledgeBaseQueryNode Query Noxus knowledge bases. ```python theme={null} kb_query = workflow_def.node("KnowledgeBaseQueryNode").config( knowledge_base_id="kb_12345", query_template="((User Question))", max_results=5, similarity_threshold=0.7, include_metadata=True, rerank_results=True ) ``` ### WorkflowCallNode Call other workflows from within a workflow. ```python theme={null} workflow_call = workflow_def.node("WorkflowCallNode").config( target_workflow_id="workflow_67890", input_mapping={ "target_input": "((source_output))" }, wait_for_completion=True, timeout_seconds=300 ) ``` ### ValidationNode Validate data against schemas or rules. ```python theme={null} validator = workflow_def.node("ValidationNode").config( validation_type="json_schema", # "json_schema", "regex", "custom" schema={ "type": "object", "required": ["name", "email"], "properties": { "name": {"type": "string", "minLength": 1}, "email": {"type": "string", "format": "email"} } }, on_validation_error="stop", # "stop", "continue", "default_value" default_value="Invalid input" ) ``` ## Node Configuration Best Practices Use consistent variable naming: ```python theme={null} # ✅ Good - descriptive names template = "Analyze ((User Input)) for ((Analysis Type))" # ❌ Bad - unclear names template = "Analyze ((Input1)) for ((Input2))" ``` Configure appropriate timeouts and retries: `python api_call = workflow_def.node("APICallNode").config( url="https://api.example.com/data", timeout_seconds=30, # Reasonable timeout retry_attempts=3, # Retry on failure retry_delay=2 # Wait between retries ) ` Set appropriate limits to prevent resource exhaustion: ```python theme={null} text_gen = workflow_def.node("TextGenerationNode").config( max_tokens=500, # Limit output length temperature=0.7, # Control randomness stop_sequences=["END"] # Define stop conditions ) ``` Use secure practices for sensitive data: ```python theme={null} # Use environment variables for secrets api_call = workflow_def.node("APICallNode").config( headers={ "Authorization": "Bearer {{API_KEY}}" # Secure variable } ) ``` ## Getting Node Information ### List Available Nodes ```python theme={null} # Get all available node types nodes = client.get_nodes() for node in nodes: print(f"Type: {node['type']}") print(f"Description: {node['description']}") print(f"Category: {node['category']}") print("---") ``` ### Get Node Configuration Schema ```python theme={null} # Find specific node type text_gen_node = next( node for node in nodes if node['type'] == 'TextGenerationNode' ) # View configuration options config_schema = text_gen_node['config_schema'] print(f"Required fields: {config_schema.get('required', [])}") print(f"Properties: {list(config_schema.get('properties', {}).keys())}") ``` ## Next Steps See complete examples using different node types Learn how to connect nodes into workflows Execute workflows and handle results Detailed API reference for workflow nodes # Workflows Overview Source: https://docs.noxus.ai/sdk/workflows/overview Learn about Noxus workflows - powerful visual programming for AI automation ## What are Workflows? Workflows in Noxus are visual, node-based programs that allow you to create complex AI automation by connecting different functional components. Think of them as flowcharts that can actually execute - each node performs a specific task, and the connections between nodes determine how data flows through your automation. Workflow Diagram Workflow Diagram ## Key Concepts Individual functional units that perform specific tasks like text generation, data processing, or logic operations Connections between nodes that define how data flows from one operation to another Data entry points and results that allow nodes to communicate with each other Settings that customize how each node behaves and processes data ## Workflow Architecture Workflows are built as **directed graphs** where: * **Nodes** represent operations (AI models, data transformations, logic gates, etc.) * **Edges** represent data flow between operations * **Execution** follows the graph structure, processing nodes when their inputs are ready ```mermaid theme={null} graph LR A[Input Node] --> B[Text Generation] B --> C[Summary Node] B --> D[Analysis Node] C --> E[Compose Text] D --> E E --> F[Output Node] ``` ## Node Types * **InputNode**: Entry points for data into your workflow * **OutputNode**: Final results and endpoints for your workflow * **FileInputNode**: Handle file uploads and processing * **TextGenerationNode**: Generate text using various AI models - **SummaryNode**: Create summaries of text content - **TranslationNode**: Translate text between languages - **EmbeddingNode**: Generate vector embeddings for text * **ComposeTextNode**: Combine multiple text inputs - **ExtractTextNode**: Extract text from documents - **DataTransformNode**: Transform and manipulate data - **FilterNode**: Filter data based on conditions * **ConditionalNode**: Branch execution based on conditions - **LoopNode**: Repeat operations over collections - **SwitchNode**: Route data based on values - **MergeNode**: Combine multiple data streams * **APICallNode**: Make HTTP requests to external services * **DatabaseQueryNode**: Query databases * **WebScraperNode**: Extract data from web pages * **EmailNode**: Send emails and notifications ## Workflow Lifecycle Create your workflow by adding nodes and connecting them to define the data flow Set up each node with the appropriate parameters and settings Ensure all connections are valid and required inputs are provided Store your workflow definition in the Noxus platform Run your workflow with input data and monitor the results Track execution progress and handle any errors or issues ## Simple Workflow Example Here's a basic workflow that takes user input and generates an AI response: ```python theme={null} from noxus_sdk.client import Client from noxus_sdk.workflows import WorkflowDefinition # Initialize client client = Client(api_key="your_api_key_here") # Create workflow definition workflow_def = WorkflowDefinition(name="Simple AI Assistant") # Add nodes input_node = workflow_def.node("InputNode").config( label="User Question", type="str" ) ai_node = workflow_def.node("TextGenerationNode").config( template="Answer this question helpfully: ((User Question))", model=["gpt-4o-mini"], temperature=0.7, max_tokens=200 ) output_node = workflow_def.node("OutputNode") # Connect nodes workflow_def.link(input_node.output(), ai_node.input("variables", "User Question")) workflow_def.link(ai_node.output(), output_node.input()) # Save workflow workflow = client.workflows.save(workflow_def) print(f"Created workflow: {workflow.id}") ``` ## Complex Workflow Example Here's a more sophisticated workflow that processes documents: ```python theme={null} # Create document processing workflow workflow_def = WorkflowDefinition(name="Document Processor") # Input nodes doc_input = workflow_def.node("FileInputNode").config( label="Document", accepted_types=["pdf", "docx", "txt"] ) topic_input = workflow_def.node("InputNode").config( label="Analysis Topic", type="str", fixed_value=True, value="key insights and recommendations" ) # Processing nodes extract_text = workflow_def.node("ExtractTextNode") summarizer = workflow_def.node("SummaryNode").config( summary_format="Bullet Points", summary_topic="Main points and conclusions", max_length=300 ) analyzer = workflow_def.node("TextGenerationNode").config( template="Analyze this document for ((Analysis Topic)):\n\n((Document Text))", model=["gpt-4o-mini"], temperature=0.3 ) # Combine results composer = workflow_def.node("ComposeTextNode").config( template="""# Document Analysis Report ## Summary ((Summary)) ## Analysis ((Analysis)) ## Generated on: {{current_date}} """ ) output_node = workflow_def.node("OutputNode") # Connect the workflow workflow_def.link(doc_input.output(), extract_text.input()) workflow_def.link(extract_text.output(), summarizer.input()) workflow_def.link(extract_text.output(), analyzer.input("variables", "Document Text")) workflow_def.link(topic_input.output(), analyzer.input("variables", "Analysis Topic")) workflow_def.link(summarizer.output(), composer.input("variables", "Summary")) workflow_def.link(analyzer.output(), composer.input("variables", "Analysis")) workflow_def.link(composer.output(), output_node.input()) # Save the workflow doc_processor = client.workflows.save(workflow_def) ``` ## Workflow Benefits Design complex logic flows without writing traditional code Create workflows once and run them multiple times with different inputs Handle large volumes of data and concurrent executions Easy to modify and update workflow logic as requirements change Share workflows with team members and build on each other's work Track execution history, performance metrics, and error rates ## Use Cases * Blog post creation with research and fact-checking * Social media content generation * Product descriptions and marketing copy * Email campaigns and newsletters * PDF analysis and summarization - Contract review and extraction - Research paper processing - Legal document analysis * Customer feedback analysis * Market research processing * Survey data interpretation * Trend analysis and reporting * Automated ticket classification - Response generation - Knowledge base queries - Escalation routing * Lead qualification * Report generation * Process automation * Decision support systems ## Best Practices * Keep workflows focused on a single purpose * Use descriptive names for nodes and connections * Group related operations together * Plan for error handling and edge cases * Minimize the number of AI model calls - Use caching for repeated operations * Process data in batches when possible - Consider parallel execution paths * Add validation nodes for input data - Include fallback paths for failures - Use conditional nodes for error routing - Log important intermediate results * Test workflows with various input types * Validate outputs match expected formats * Monitor execution times and resource usage * Version control your workflow definitions ## Getting Started Ready to build your first workflow? Here's what to do next: Learn the fundamentals of creating workflows with the SDK Execute workflows and handle results Explore all available node types and their configurations See real-world workflow examples and patterns ## Advanced Topics Once you're comfortable with basic workflows, explore advanced topics like conditional logic, loops, external integrations, and workflow optimization techniques. # Running Workflows Source: https://docs.noxus.ai/sdk/workflows/running-workflows Learn how to execute workflows, monitor progress, and handle results ## Overview Once you've built and saved a workflow, you can execute it with different inputs and monitor its progress. The Noxus SDK provides both synchronous and asynchronous methods for running workflows and handling results. ## Basic Workflow Execution ### Running a Workflow ```python theme={null} from noxus_sdk.client import Client client = Client(api_key="your_api_key_here") # Get an existing workflow workflow = client.workflows.get("workflow_id_here") # Run with input data run = workflow.run(body={ "User Input": "What are the benefits of renewable energy?", "Analysis Type": "comprehensive" }) # Wait for completion result = run.wait(interval=2) # Check every 2 seconds print(f"Status: {result.status}") print(f"Output: {result.output}") ``` ### Input Formats The input format depends on how you configured your workflow nodes: Use the label you assigned to input nodes: ```python theme={null} # If you created an input node with label "User Question" input_node = workflow_def.node("InputNode").config( label="User Question" ) # Run with this input run = workflow.run(body={ "User Question": "How does photosynthesis work?" }) ``` Use the node's unique ID: ```python theme={null} # Get node ID from workflow definition input_node = workflow_def.node("InputNode") node_id = input_node.id # Run with node ID run = workflow.run(body={ node_id: "Your input value here" }) ``` Use the format `{node_id}::{input_name}`: ```python theme={null} # For input nodes, the default input name is "input" run = workflow.run(body={ f"{node_id}::input": "Your input value here" }) ``` ## Monitoring Execution ### Synchronous Monitoring Wait for workflow completion with custom intervals: ```python theme={null} # Run workflow run = workflow.run(body={"input": "test data"}) # Monitor with custom interval and timeout try: result = run.wait( interval=5, # Check every 5 seconds timeout=300 # Maximum wait time (5 minutes) ) print(f"Completed in {result.execution_time}ms") print(f"Final output: {result.output}") except TimeoutError: print("Workflow execution timed out") print(f"Current status: {run.status}") ``` ### Asynchronous Monitoring For non-blocking execution monitoring: ```python theme={null} import asyncio async def monitor_workflow_async(): client = Client(api_key="your_api_key_here") workflow = await client.workflows.aget("workflow_id") # Start workflow execution run = await workflow.arun(body={"input": "test data"}) # Monitor asynchronously result = await run.a_wait(interval=2) return result # Run async monitoring result = asyncio.run(monitor_workflow_async()) ``` ### Manual Status Checking For more control over monitoring: ```python theme={null} import time # Start workflow run = workflow.run(body={"input": "test data"}) # Manual monitoring loop while True: # Refresh run status run = run.refresh() print(f"Status: {run.status}") if run.status in ["completed", "failed", "cancelled"]: break time.sleep(3) # Wait 3 seconds before next check # Handle final result if run.status == "completed": print(f"Success! Output: {run.output}") elif run.status == "failed": print(f"Failed: {run.error_message}") ``` ## Real-Time Streaming Instead of polling for run completion, you can stream events in real time using Server-Sent Events (SSE). This gives you instant notifications as nodes execute and when the run finishes. ### Streaming Run Events ```python theme={null} # Create a run and stream events as they happen run = workflow.run(body={"User Input": "Explain quantum computing"}) for event in run.stream(): if event.type == "content": node_id = event.data.get("id") status = event.data.get("status") content = event.data.get("content", "") print(f"[{node_id}] {status}: {content}") elif event.type == "state": print(f"Progress: {event.data.get('status')}") if event.is_terminal: print(f"Run finished: {event.data['workflow_status']}") # Refresh to get final output after streaming run.refresh() print(f"Output: {run.output}") ``` ### One-Liner: Run and Stream Create a run and immediately stream its events in a single call: ```python theme={null} for event in workflow.run_and_stream(body={"User Input": "Hello"}): if event.type == "content" and event.data.get("content"): print(event.data["content"], end="") if event.is_terminal: print(f"\nDone: {event.data['workflow_status']}") ``` ### Async Streaming ```python theme={null} import asyncio async def stream_workflow(): client = Client(api_key="your_api_key_here") workflow = await client.workflows.aget("workflow_id") async for event in workflow.arun_and_stream(body={"input": "test"}): if event.type == "content": print(event.data) if event.is_terminal: break asyncio.run(stream_workflow()) ``` ### RunEvent Properties Each event yielded by `stream()` is a `RunEvent` object: | Property | Type | Description | | ------------------- | ------ | ------------------------------------------------------------ | | `event.type` | `str` | `"content"` (node output) or `"state"` (progress) | | `event.data` | `dict` | Event payload with node ID, status, content, etc. | | `event.is_terminal` | `bool` | `True` when `workflow_status` is `"completed"` or `"failed"` | | `event.redis_id` | `str` | Stream cursor — pass as `etag` to resume from this point | `run.wait()` now uses SSE streaming internally for instant completion detection. If the SSE endpoint is unavailable, it automatically falls back to polling. ## Webhook Callbacks For event-driven integrations, you can provide a `callback_url` when creating a run. The platform will POST the run result to your URL when the run reaches a terminal state (completed, failed, or stopped). ### Setting Up a Callback ```python theme={null} run = workflow.run( body={"User Input": "Analyze this data"}, callback_url="https://your-server.com/webhook/noxus" ) # No need to poll — your server will be notified print(f"Run {run.id} started, webhook will fire on completion") ``` ### Webhook Payload Your endpoint receives a POST with this JSON body: ```json theme={null} { "run_id": "abc-123", "workflow_id": "wf-456", "status": "completed", "outputs": { "node_id::output": { "text": "The generated result...", "file": null } }, "error": null, "started_at": "2025-01-15T10:30:00.000000", "completed_at": "2025-01-15T10:30:05.000000", "duration_ms": 5000 } ``` ### Webhook Behavior * **Retries**: Failed deliveries are retried up to 3 times with exponential backoff (1s, 5s, 15s) * **Validation**: The `callback_url` must be a valid HTTP(S) URL — invalid URLs are rejected at creation time * **Non-blocking**: Webhook delivery never affects run execution — if your endpoint is down, the run still completes normally * **Terminal states**: Webhooks fire on `completed`, `failed`, and `stopped` statuses ## Handling Different Input Types ### Text Inputs ```python theme={null} # Simple text input run = workflow.run(body={ "Question": "Explain quantum computing", "Context": "For a general audience" }) ``` ### File Inputs ```python theme={null} # Method 1: Using a public file URL (downloaded server-side, max 25MB). # Must be publicly reachable — private/loopback/metadata hosts are rejected. run = workflow.run(body={ "Document": {"uri": "https://path/to/file.pdf", "name": "file.pdf"} }) # Method 2: Using base64-encoded file content (data URI format) import base64 with open("document.pdf", "rb") as file: file_content = file.read() base64_content = base64.b64encode(file_content).decode("utf-8") data_uri = f"data:application/pdf;base64,{base64_content}" run = workflow.run(body={ "Document": {"uri": data_uri, "name": "document.pdf"} }) # Method 3: Reference a file already uploaded via POST /v1/file run = workflow.run(body={ "Document": {"uri": "spot://your-file-id", "name": "file.pdf"} }) ``` ### Multiple Inputs ```python theme={null} # Workflow with multiple input nodes run = workflow.run(body={ "Primary Text": "The main content to analyze", "Analysis Focus": "sentiment and themes", "Output Format": "bullet points", "Max Length": "300 words" }) ``` ### Fixed vs Dynamic Inputs ```python theme={null} # Only provide values for dynamic inputs # Fixed inputs (configured with fixed_value=True) are automatically used run = workflow.run(body={ "User Question": "What is machine learning?" # Fixed inputs like system prompts are handled automatically }) # Override fixed inputs if needed (if the workflow allows it) run = workflow.run(body={ "User Question": "What is machine learning?", "System Prompt": "You are a technical expert. Be precise and detailed." }) ``` ## Result Handling ### Understanding Run Results ```python theme={null} result = run.wait() # Basic result information print(f"Run ID: {result.id}") print(f"Status: {result.status}") print(f"Started: {result.created_at}") print(f"Completed: {result.updated_at}") print(f"Execution time: {result.execution_time}ms") # Output data print(f"Final output: {result.output}") # Error information (if failed) if result.status == "failed": print(f"Error: {result.error_message}") print(f"Error details: {result.error_details}") ``` ### Processing Output Data ```python theme={null} # Output is typically a string, but can be structured data output = result.output if isinstance(output, str): # Text output print(f"Generated text: {output}") elif isinstance(output, dict): # Structured output for key, value in output.items(): print(f"{key}: {value}") elif isinstance(output, list): # List output for i, item in enumerate(output): print(f"Item {i}: {item}") ``` #### Large or Truncated Text Outputs When a text output exceeds **16,000 characters**, the API does not return the full string inline. Instead, it truncates the value in the response, uploads the full content to file storage, and returns a `TextContainer`-shaped dict with a `has_preview` flag and a `file` reference: ```python theme={null} { "text": "...first 16,000 characters of the output...", "has_preview": True, "file": { "id": "abc-123", "uri": "spot://abc-123", "name": "truncated_result.txt", "content_type": "text/plain", "size": 52314 } } ``` Detect this shape via `has_preview` and fetch the full content through the Files API: ```python theme={null} result = run.wait() for key, value in result.output.items(): if isinstance(value, dict) and value.get("has_preview"): # Output was truncated — download the full content from storage file_id = value["file"]["id"] full_content = client.files.get(file_id).decode("utf-8") print(f"{key}: full length {len(full_content)} chars") else: # Output fit under the limit — use `text` (or the raw string) directly text = value.get("text") if isinstance(value, dict) else value print(f"{key}: {text}") ``` `has_preview` only appears on outputs that were actually truncated. Outputs that fit under the 16,000-character limit are returned either as a plain string or as `{"text": "..."}` without a `file` reference, so always guard on `has_preview` before attempting to download. ### Saving Results ```python theme={null} import json from datetime import datetime # Save result to file result_data = { "workflow_id": workflow.id, "run_id": result.id, "timestamp": datetime.now().isoformat(), "status": result.status, "output": result.output, "execution_time": result.execution_time } with open(f"workflow_result_{result.id}.json", "w") as f: json.dump(result_data, f, indent=2) print(f"Result saved to workflow_result_{result.id}.json") ``` ## Batch Processing ### Running Multiple Workflows ```python theme={null} import asyncio async def run_multiple_workflows(): client = Client(api_key="your_api_key_here") # Get workflows workflow1 = await client.workflows.aget("workflow_id_1") workflow2 = await client.workflows.aget("workflow_id_2") # Start multiple runs concurrently run1 = await workflow1.arun(body={"input": "data for workflow 1"}) run2 = await workflow2.arun(body={"input": "data for workflow 2"}) # Wait for all to complete result1, result2 = await asyncio.gather( run1.a_wait(), run2.a_wait() ) return result1, result2 # Execute batch processing results = asyncio.run(run_multiple_workflows()) ``` ### Processing Multiple Inputs ```python theme={null} async def process_multiple_inputs(workflow, inputs): """Process multiple inputs through the same workflow""" # Start all runs runs = [] for input_data in inputs: run = await workflow.arun(body=input_data) runs.append(run) # Wait for all to complete results = [] for run in runs: result = await run.a_wait() results.append(result) return results # Usage inputs = [ {"Question": "What is AI?"}, {"Question": "How does machine learning work?"}, {"Question": "What are neural networks?"} ] workflow = client.workflows.get("qa_workflow_id") results = asyncio.run(process_multiple_inputs(workflow, inputs)) for i, result in enumerate(results): print(f"Answer {i+1}: {result.output}") ``` ## Error Handling ### Handling Execution Errors ```python theme={null} def run_workflow_safely(workflow, input_data): """Run workflow with comprehensive error handling""" try: # Start the workflow run = workflow.run(body=input_data) # Wait for completion with timeout result = run.wait(interval=2, timeout=600) # 10 minute timeout if result.status == "completed": return {"success": True, "output": result.output} elif result.status == "failed": return { "success": False, "error": result.error_message, "details": result.error_details } else: return {"success": False, "error": f"Unexpected status: {result.status}"} except TimeoutError: return {"success": False, "error": "Workflow execution timed out"} except Exception as e: return {"success": False, "error": f"Execution error: {str(e)}"} # Usage result = run_workflow_safely(workflow, {"input": "test data"}) if result["success"]: print(f"Success: {result['output']}") else: print(f"Error: {result['error']}") ``` ### Retry Logic ```python theme={null} import time import random def run_workflow_with_retry(workflow, input_data, max_retries=3): """Run workflow with exponential backoff retry""" for attempt in range(max_retries): try: run = workflow.run(body=input_data) result = run.wait(interval=2, timeout=300) if result.status == "completed": return result elif result.status == "failed": if attempt == max_retries - 1: raise Exception(f"Workflow failed: {result.error_message}") # Wait before retry with exponential backoff wait_time = (2 ** attempt) + random.uniform(0, 1) print(f"Attempt {attempt + 1} failed, retrying in {wait_time:.1f}s...") time.sleep(wait_time) except Exception as e: if attempt == max_retries - 1: raise wait_time = (2 ** attempt) + random.uniform(0, 1) print(f"Error on attempt {attempt + 1}: {e}") print(f"Retrying in {wait_time:.1f}s...") time.sleep(wait_time) # Usage try: result = run_workflow_with_retry(workflow, {"input": "test data"}) print(f"Success after retries: {result.output}") except Exception as e: print(f"Failed after all retries: {e}") ``` ## Performance Optimization ### Efficient Polling ```python theme={null} def adaptive_wait(run, initial_interval=1, max_interval=30, timeout=600): """Wait for workflow completion with adaptive polling""" start_time = time.time() interval = initial_interval while True: run = run.refresh() if run.status in ["completed", "failed", "cancelled"]: return run # Check timeout if time.time() - start_time > timeout: raise TimeoutError("Workflow execution timed out") # Adaptive interval - increase polling interval over time time.sleep(interval) interval = min(interval * 1.5, max_interval) # Usage run = workflow.run(body={"input": "test data"}) result = adaptive_wait(run) ``` ### Concurrent Execution with Limits ```python theme={null} import asyncio from asyncio import Semaphore async def run_workflows_with_limit(workflows_and_inputs, max_concurrent=5): """Run multiple workflows with concurrency limit""" semaphore = Semaphore(max_concurrent) async def run_single(workflow, input_data): async with semaphore: run = await workflow.arun(body=input_data) return await run.a_wait() # Create tasks for all workflows tasks = [ run_single(workflow, input_data) for workflow, input_data in workflows_and_inputs ] # Execute with concurrency limit results = await asyncio.gather(*tasks, return_exceptions=True) return results # Usage workflows_and_inputs = [ (workflow1, {"input": "data1"}), (workflow2, {"input": "data2"}), (workflow3, {"input": "data3"}), # ... more workflows ] results = asyncio.run(run_workflows_with_limit(workflows_and_inputs)) ``` ## Best Practices Validate inputs before running workflows: ```python theme={null} def validate_workflow_input(input_data, required_fields): """Validate workflow input data""" missing_fields = [] for field in required_fields: if field not in input_data or not input_data[field]: missing_fields.append(field) if missing_fields: raise ValueError(f"Missing required fields: {missing_fields}") return True # Usage required_fields = ["Question", "Context"] validate_workflow_input(input_data, required_fields) ``` Manage resources efficiently: ```python theme={null} # Use context managers for resource cleanup async def process_workflows(): client = Client(api_key="your_api_key") try: # Process workflows results = await run_multiple_workflows() return results finally: # Cleanup resources if needed pass ``` Implement comprehensive logging: ```python theme={null} import logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) def run_workflow_with_logging(workflow, input_data): logger.info(f"Starting workflow {workflow.id}") logger.info(f"Input data: {input_data}") start_time = time.time() run = workflow.run(body=input_data) logger.info(f"Workflow started, run ID: {run.id}") result = run.wait() execution_time = time.time() - start_time logger.info(f"Workflow completed in {execution_time:.2f}s") logger.info(f"Status: {result.status}") if result.status == "failed": logger.error(f"Workflow failed: {result.error_message}") return result ``` ## Next Steps Learn about all available node types and their capabilities Explore complete workflow examples for common use cases Master advanced workflow design and execution patterns Detailed API reference for workflow execution