# API Reference: api.riotpiao.com Gateway to all homelab services. Single entry point for LLM inference, workflows, queues, memory, and cluster operations. **Base URL:** `https://api.riotpiao.com` --- ## Table of Contents 1. [Health & Status](#health--status) 2. [LLM Services](#llm-services) 3. [Workflow Services (Temporal)](#workflow-services-temporal) 4. [Queue Services (SQS)](#queue-services-sqs) 5. [Memory Services](#memory-services) 6. [IAM Services](#iam-services) 7. [S3 Services](#s3-services) 8. [Authentication](#authentication) 9. [Error Handling](#error-handling) --- ## Health & Status ### GET /healthz Liveness probe. Returns immediately without checks. ```bash curl https://api.riotpiao.com/healthz ``` **Response:** ```json { "status": "alive" } ``` **Status Code:** 200 --- ### GET /readyz Readiness probe. Returns 200 when gateway is ready (config loaded, connections available). ```bash curl https://api.riotpiao.com/readyz ``` **Response:** ```json { "status": "ready" } ``` **Status Code:** 200 (ready) or 503 (not ready) --- ## LLM Services ### GET /v1/models List all available models for inference. ```bash curl https://api.riotpiao.com/v1/models ``` **Response:** ```json { "object": "list", "data": [ { "id": "reasoning", "object": "model", "owned_by": "api.riotpiao.com", "created": 1700000000 }, { "id": "ornith:35b", "object": "model", "owned_by": "api.riotpiao.com", "created": 1700000000 }, { "id": "qwen2.5:3b-instruct", "object": "model", "owned_by": "api.riotpiao.com", "created": 1700000000 }, { "id": "nomic-ai/nomic-embed-text-v2-moe", "object": "model", "owned_by": "api.riotpiao.com", "created": 1700000000 }, { "id": "BAAI/bge-reranker-base", "object": "model", "owned_by": "api.riotpiao.com", "created": 1700000000 } ] } ``` **Status Code:** 200 --- ### POST /v1/chat/completions Chat with an LLM model. Supports streaming, tool calling, and multi-turn conversations. ```bash curl -X POST https://api.riotpiao.com/v1/chat/completions \ -H 'Content-Type: application/json' \ -d '{ "model": "reasoning", "messages": [ {"role": "user", "content": "What is 2+2?"} ] }' ``` **Request Body:** ```json { "model": "reasoning", "messages": [ { "role": "user", "content": "string or array" } ], "temperature": 0.7, "top_p": 1.0, "max_tokens": 2048, "stream": false, "tools": [ { "type": "function", "function": { "name": "function_name", "description": "What it does", "parameters": { "type": "object", "properties": {}, "required": [] } } } ] } ``` **Response (non-streaming):** ```json { "id": "chatcmpl-123", "object": "chat.completion", "created": 1704000000, "model": "reasoning", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "2+2=4" }, "finish_reason": "stop" } ], "usage": { "prompt_tokens": 10, "completion_tokens": 5, "total_tokens": 15 } } ``` **Response (streaming, SSE format):** ``` data: {"id":"...","object":"chat.completion.chunk","choices":[{"delta":{"content":"2"}}]} data: {"id":"...","object":"chat.completion.chunk","choices":[{"delta":{"content":"+"}}]} data: {"id":"...","object":"chat.completion.chunk","choices":[{"delta":{"content":"2"}}]} data: {"id":"...","object":"chat.completion.chunk","choices":[{"delta":{"content":"="}}]} data: {"id":"...","object":"chat.completion.chunk","choices":[{"delta":{"content":"4"}}]} data: [DONE] ``` **Status Codes:** - 200 OK - 400 Bad Request (invalid model, missing fields, invalid JSON) - 500 Internal Server Error (upstream issue) **Available Models:** - `reasoning` — DeepSeek-R1-Distill reasoning model (8 concurrent slots) - `ornith:35b` — Ollama 35B model - `qwen2.5:3b-instruct` — Qwen 2.5 3B model --- ### POST /v1/embeddings Generate text embeddings. ```bash curl -X POST https://api.riotpiao.com/v1/embeddings \ -H 'Content-Type: application/json' \ -d '{ "model": "nomic-ai/nomic-embed-text-v2-moe", "input": ["text to embed", "another text"] }' ``` **Request Body:** ```json { "model": "nomic-ai/nomic-embed-text-v2-moe", "input": "string or array of strings", "encoding_format": "float" } ``` **Response:** ```json { "object": "list", "data": [ { "object": "embedding", "embedding": [0.1, 0.2, 0.3, ...], "index": 0 }, { "object": "embedding", "embedding": [0.4, 0.5, 0.6, ...], "index": 1 } ], "model": "nomic-ai/nomic-embed-text-v2-moe", "usage": { "prompt_tokens": 20, "total_tokens": 20 } } ``` **Status Codes:** - 200 OK - 400 Bad Request (invalid model or input) - 500 Internal Server Error --- ### POST /v1/rerank Rerank documents by relevance to a query. ```bash curl -X POST https://api.riotpiao.com/v1/rerank \ -H 'Content-Type: application/json' \ -d '{ "model": "BAAI/bge-reranker-base", "query": "machine learning", "texts": [ "Machine learning is AI", "Python is a language", "Deep learning is ML" ], "top_k": 2 }' ``` **Request Body:** ```json { "model": "BAAI/bge-reranker-base", "query": "search query", "texts": ["text1", "text2", "text3"], "top_k": 2, "return_documents": true } ``` **Response:** ```json { "results": [ { "index": 0, "score": 0.95, "text": "Machine learning is AI" }, { "index": 2, "score": 0.85, "text": "Deep learning is ML" } ] } ``` **Status Codes:** - 200 OK - 400 Bad Request - 500 Internal Server Error --- ## Workflow Services (Temporal) Base path: `/workflow` All operations use REST with JSON body. Internally translated to gRPC (port 7233). ### POST /workflow Execute a workflow operation via action dispatch. ```bash curl -X POST https://api.riotpiao.com/workflow \ -H 'Content-Type: application/json' \ -d '{ "action": "START_WORKFLOW", "namespace": "default", "payload": { "workflow_id": "order-123", "workflow_type": "ProcessOrder", "task_queue": "orders_queue", "input": { "order_id": "123", "amount": 99.99 } } }' ``` **Supported Actions:** #### START_WORKFLOW Start a new workflow execution. **Payload:** ```json { "workflow_id": "unique-id", "workflow_type": "WorkflowName", "task_queue": "queue_name", "input": {"field": "value"}, "options": { "workflow_execution_timeout": 3600, "workflow_run_timeout": 1800, "workflow_task_timeout": 300 } } ``` **Response:** ```json { "success": true, "action": "START_WORKFLOW", "data": { "workflow_id": "order-123", "run_id": "abc123def", "start_time": "2026-08-27T..." } } ``` #### DESCRIBE_WORKFLOW Get workflow execution details. **Payload:** ```json { "workflow_id": "order-123", "run_id": "abc123def" } ``` **Response:** ```json { "success": true, "action": "DESCRIBE_WORKFLOW", "data": { "workflow_id": "order-123", "run_id": "abc123def", "status": "RUNNING", "start_time": "2026-08-27T..." } } ``` #### LIST_WORKFLOWS List workflow executions with filtering. **Payload:** ```json { "status": "RUNNING", "page_size": 50, "next_page_token": "" } ``` #### GET_WORKFLOW_HISTORY Retrieve workflow execution history (events). **Payload:** ```json { "workflow_id": "order-123", "run_id": "abc123def", "max_events": 100 } ``` #### SIGNAL_WORKFLOW Send a signal to a running workflow (trigger handler). **Payload:** ```json { "workflow_id": "order-123", "run_id": "abc123def", "signal_name": "payment_received", "input": {"amount": 99.99} } ``` #### QUERY_WORKFLOW Query workflow state without modifying it (read-only). **Payload:** ```json { "workflow_id": "order-123", "run_id": "abc123def", "query_type": "get_status" } ``` #### CANCEL_WORKFLOW Request graceful cancellation. **Payload:** ```json { "workflow_id": "order-123", "run_id": "abc123def" } ``` #### TERMINATE_WORKFLOW Stop workflow immediately. **Payload:** ```json { "workflow_id": "order-123", "run_id": "abc123def", "reason": "User cancelled" } ``` #### Other Actions - RESET_WORKFLOW - UPDATE_WORKFLOW - HEARTBEAT_ACTIVITY - COMPLETE_ACTIVITY - FAIL_ACTIVITY **Status Codes:** - 200 OK - 400 Bad Request (invalid action or fields) - 404 Not Found (workflow not found) - 503 Service Unavailable (Temporal unreachable) --- ## Queue Services (SQS) Base path: `/sqs` (via X-Service header routing) Operations via ServiceAdapter CRD. Use header-based dispatch: ```bash curl -X GET https://api.riotpiao.com/ \ -H 'X-Service: sqs' \ -H 'X-Resource: queue/my-queue/messages' ``` **Supported Resources:** - `queue` — List queues - `queue/{name}/message` — Send/receive messages - `queue/{name}/messages` — Batch operations --- ## Memory Services Base path: `/` (via X-Service header) Query and manage semantic memory for knowledge retrieval. ```bash curl -X GET https://api.riotpiao.com/ \ -H 'X-Service: memory' \ -H 'X-Resource: query' \ -G --data-urlencode 'query=explain machine learning' \ --data-urlencode 'level=L1,L2' ``` **Supported Resources:** - `query` — Search memory (GET with params) - `skill` — List/retrieve skills - `project` — List/describe projects - `ingest` — Add new documents (POST, requires memory:write capability) --- ## IAM Services Base path: `/` (via X-Service header) Manage Authentik users, groups, and permissions. ```bash curl -X GET https://api.riotpiao.com/ \ -H 'Authorization: Bearer ' \ -H 'X-Service: iam' \ -H 'X-Resource: user' ``` **Supported Resources:** - `user` — List/create users - `user/{id}` — Get/update/delete specific user - `role` (group) — Manage groups/roles - `permission` — List permissions - `flow` — List authentication flows --- ## S3 Services Base path: `/` (via X-Service header) Object storage operations via MinIO. ```bash curl -X GET https://api.riotpiao.com/ \ -H 'X-Service: s3' \ -H 'X-Resource: bucket/my-bucket/objects' ``` **Supported Resources:** - `bucket` — List buckets - `bucket/{name}/objects` — List objects in bucket - `bucket/{name}/object/{key}` — Get/put/delete object --- ## Authentication ### Bearer Token (JWT) All operations except `/healthz` and `/readyz` require authentication. ```bash curl -H 'Authorization: Bearer ' \ https://api.riotpiao.com/v1/models ``` ### Obtaining Tokens **Via Authentik OIDC (human login):** ```bash core auth login --username user@example.com ``` **Via service account (programmatic):** ```bash core mwinit login --username service-account --password secret export RIOTPIAO_TOKEN=$(cat ~/.talos/.riotpiao-auth) curl -H "Authorization: Bearer $RIOTPIAO_TOKEN" \ https://api.riotpiao.com/v1/models ``` ### Capabilities (RBAC) Tokens embed capabilities in claims. Required capabilities: - `llm:inference` — `/v1/*` chat/embeddings/rerank - `workflow:execute` — `/workflow` operations - `memory:read` — Memory queries - `memory:write` — Memory ingest - `sqs:access` — Queue operations - `s3:access` — S3 operations - `iam:admin` — IAM management --- ## Error Handling ### Problem Details (RFC 9457) All error responses use standardized JSON format: ```json { "type": "https://api.example.com/problems/unknown-model", "title": "Unknown Model", "status": 400, "detail": "Model 'gpt-4' is not available. See valid_models for available options.", "valid_models": ["reasoning", "ornith:35b", ...] } ``` ### HTTP Status Codes | Status | Meaning | |--------|---------| | 200 | Success | | 400 | Bad Request (validation error, invalid model, missing fields) | | 401 | Unauthorized (invalid/missing token) | | 403 | Forbidden (insufficient permissions/capability) | | 404 | Not Found (workflow, resource) | | 409 | Conflict (duplicate workflow_id) | | 429 | Too Many Requests (rate limit exceeded) | | 503 | Service Unavailable (backend unreachable) | | 504 | Gateway Timeout (request exceeded timeout) | ### Error Examples **Unknown Model:** ```bash curl -X POST https://api.riotpiao.com/v1/chat/completions \ -d '{"model":"gpt-4","messages":[]}' # Returns 400 { "type": "https://api.example.com/problems/unknown-model", "title": "Unknown Model", "status": 400, "detail": "Model 'gpt-4' is not available", "valid_models": ["reasoning", "ornith:35b", "qwen2.5:3b-instruct", ...] } ``` **Temporal Unavailable:** ```bash curl -X POST https://api.riotpiao.com/workflow \ -d '{"action":"START_WORKFLOW",...}' # Returns 503 if Temporal server unreachable { "success": false, "error": "TEMPORAL_UNAVAILABLE", "message": "Temporal server connection not available" } ``` --- ## Examples ### Complete RAG Pipeline 1. **Query embedding:** ```bash curl -X POST https://api.riotpiao.com/v1/embeddings \ -H 'Authorization: Bearer $TOKEN' \ -d '{ "model": "nomic-ai/nomic-embed-text-v2-moe", "input": "how do neural networks work" }' | jq '.data[0].embedding' ``` 2. **Rerank documents:** ```bash curl -X POST https://api.riotpiao.com/v1/rerank \ -H 'Authorization: Bearer $TOKEN' \ -d '{ "model": "BAAI/bge-reranker-base", "query": "how do neural networks work", "texts": ["doc1", "doc2", "doc3"], "top_k": 2 }' ``` 3. **Chat with context:** ```bash curl -X POST https://api.riotpiao.com/v1/chat/completions \ -H 'Authorization: Bearer $TOKEN' \ -d '{ "model": "reasoning", "messages": [ { "role": "user", "content": "Based on these documents: [top-2 from rerank]\nQuestion: how do neural networks work?" } ] }' ``` ### Workflow Orchestration ```bash # Start workflow WORKFLOW_ID="order-$(date +%s)" curl -X POST https://api.riotpiao.com/workflow \ -H 'Authorization: Bearer $TOKEN' \ -d "{ \"action\": \"START_WORKFLOW\", \"namespace\": \"default\", \"payload\": { \"workflow_id\": \"$WORKFLOW_ID\", \"workflow_type\": \"ProcessOrder\", \"task_queue\": \"orders\", \"input\": {\"order_id\": \"123\"} } }" # Monitor execution curl -X POST https://api.riotpiao.com/workflow \ -H 'Authorization: Bearer $TOKEN' \ -d "{ \"action\": \"DESCRIBE_WORKFLOW\", \"namespace\": \"default\", \"payload\": {\"workflow_id\": \"$WORKFLOW_ID\"} }" # Send signal when payment received curl -X POST https://api.riotpiao.com/workflow \ -H 'Authorization: Bearer $TOKEN' \ -d "{ \"action\": \"SIGNAL_WORKFLOW\", \"namespace\": \"default\", \"payload\": { \"workflow_id\": \"$WORKFLOW_ID\", \"signal_name\": \"payment_received\", \"input\": {\"amount\": 99.99} } }" ``` --- ## Rate Limits & Quotas - `/v1/chat/completions` — 8 concurrent slots (reasoning model) - `/v1/embeddings` — 10 concurrent - `/v1/rerank` — 10 concurrent - `/workflow` — 100 concurrent operations - Retry-After header set on 429 responses --- ## Timeouts | Endpoint | Connect | Read | Write | |----------|---------|------|-------| | `/v1/chat/completions` | 10s | 1h | 1h | | `/v1/embeddings` | 10s | 10m | 10m | | `/v1/rerank` | 10s | 10m | 10m | | `/workflow` | 10s | 30s | 10s | --- ## Support - **Health:** `curl https://api.riotpiao.com/healthz` - **Readiness:** `curl https://api.riotpiao.com/readyz` - **Models:** `curl https://api.riotpiao.com/v1/models` - **Logs:** `kubectl -n api logs deployment/homelab-frontend` --- ## LLM Inference in Workflows The Poimen workflows system includes built-in LLM inference activities that call `/v1/chat/completions` via the gateway. ### LLMInferenceActivity Single-prompt LLM inference within a workflow. **Workflow Definition (Canvas Node):** ```json { "id": "llm-node-1", "type": "llm-inference", "label": "Analyze Code with LLM", "data": { "model": "reasoning", "system_prompt": "You are a code analysis expert. Provide detailed feedback.", "user_prompt": "Analyze this code for security issues: {{ previous_output.code }}", "temperature": 0.7, "max_tokens": 2048, "auth_token": "{{ user.jwt_token }}" } } ``` **Fields:** - `model` (required): Model ID (reasoning, ornith:35b, ornith:13b, qwen2.5:3b) - `system_prompt`: System instruction for the model - `user_prompt` (required): User message to send - `temperature`: Sampling temperature (0.0-1.0, default 0.7) - `max_tokens`: Maximum output tokens - `auth_token` (optional): JWT token for authenticated endpoints (propagates as Authorization: Bearer header) **Backend Implementation:** The LLMInferenceActivity in the workflows backend automatically: 1. Substitutes template variables (e.g., `{{ previous_output.code }}`) 2. Calls `/v1/chat/completions` with the resolved prompt 3. Returns the LLM response as activity output 4. Retries on transient failures (up to 3 attempts) 5. Timeouts after 120 seconds **Output:** ```json { "response": "The code has several security vulnerabilities...", "model": "reasoning", "stop_reason": "stop_sequence", "tokens_used": 450 } ``` **Supported Models:** - `reasoning` — DeepSeek-R1-Distill (best for complex analysis) - `ornith:35b` — Ollama 35B - `ornith:13b` — Ollama 13B - `qwen2.5:3b` — Qwen 2.5 3B --- ### LLMBatchInferenceActivity Multiple-prompt LLM inference (sequential processing). **Workflow Definition:** ```json { "id": "llm-batch-1", "type": "llm-batch-inference", "label": "Batch Code Review", "data": { "model": "reasoning", "system_prompt": "Review each code snippet and provide feedback.", "prompts": [ "Review snippet 1: {{ files[0].content }}", "Review snippet 2: {{ files[1].content }}", "Review snippet 3: {{ files[2].content }}" ], "auth_token": "{{ user.jwt_token }}" } } ``` **Fields:** - `model` (required): Model ID - `system_prompt`: System instruction (same for all prompts) - `prompts` (required): List of user prompts to process - `temperature`: Sampling temperature (0.0-1.0) - `auth_token` (optional): JWT token for authenticated endpoints (propagates as Authorization: Bearer header) **Output:** ```json { "responses": [ "Snippet 1 review...", "Snippet 2 review...", "Snippet 3 review..." ], "model": "reasoning", "errors": [] } ``` **Typical Use Cases:** - Batch code review across multiple files - Parallel document summarization - Comparative analysis of alternatives - Policy compliance checking --- ### Workflow Integration Examples **1. Code Analysis Workflow** ``` Clone Repo → Analyze Code → LLM Security Review → Generate Report → Notify ``` **2. Document Processing** ``` Retrieve Documents → Embed + Index → LLM Summarize (batch) → Archive ``` **3. Multi-Stage Review** ``` Retrieve Memory → LLM Context Extraction → Route to Activity A/B/C → Notify ``` --- ### Authentication & Authorization JWT tokens can be passed to LLM inference activities and are automatically propagated to the LLM API endpoint. **Token Flow:** ``` Workflow Canvas ↓ (auth_token field) Poimen Workflow Executor ↓ (passed to LLMInferenceActivity) Activity calls LLM client ↓ (adds "Authorization: Bearer {token}" header) homelab-frontend proxy ↓ (preserves Authorization header) LLM Backend (reasoning/ollama/etc) ↓ (validates token) Response returned ``` **Example: Passing User Token from RetrieveMemory Activity** ```json { "id": "flow-1", "type": "retrieve-memory", "label": "Get User Context", "data": {...} } → { "id": "llm-1", "type": "llm-inference", "label": "Analyze with User's Token", "data": { "model": "reasoning", "user_prompt": "...", "auth_token": "{{ previous_output.user_token }}" } } ``` **Token Validation:** - Tokens are validated by homelab-frontend proxy (checks signature, expiration) - Only valid tokens are propagated to LLM backend - Invalid tokens result in 401 Unauthorized error - Missing token (if required) results in 401 Unauthorized **Note:** The `auth_token` field is optional. If omitted, the LLM API is called without authentication (public endpoints only). --- ### CanvasReasonerActivity Auto-suggest workflow connections using LLM reasoning. When you drop new activities onto the canvas, this activity analyzes them and suggests logical connections based on input/output compatibility and workflow patterns. **Workflow Definition:** ```json { "id": "canvas-reason-1", "type": "canvas-reasoner", "label": "Auto-Connect Activities", "data": { "nodes": "{{ workflow.nodes }}", "edges": "{{ workflow.edges }}", "preserve_existing": true, "auth_token": "{{ user.jwt_token }}" } } ``` **Use Cases:** - New nodes added to canvas → automatically suggest connections - Validate workflow design → LLM reasoning explains connections - Redesign workflow → suggest optimal activity sequence - Data flow analysis → ensure proper input/output matching **How It Works:** 1. Analyzes all node types and their configurations 2. Reviews existing edges (if preserving) 3. Uses reasoning model to infer logical connections 4. Returns suggested edges with confidence score 5. Includes reasoning explanation **Output Example:** ```json { "suggested_edges": [ {"source": "clone-1", "target": "analyze-1"}, {"source": "analyze-1", "target": "security-scan-1"}, {"source": "security-scan-1", "target": "report-1"} ], "reasoning": "Clone repository first, analyze code, perform security scan, generate report. Standard code review workflow.", "confidence": 0.92 } ``` **Fields:** - `nodes` (required): Canvas nodes to analyze - `edges` (required): Current edges - `preserve_existing` (optional, default true): Keep existing edges and only suggest new ones - `auth_token` (optional): JWT for LLM reasoning calls **Confidence Scores:** - 0.9-1.0: High confidence (common patterns) - 0.7-0.9: Medium confidence (reasonable connections) - 0.5-0.7: Low confidence (multiple valid approaches) - <0.5: Unsure (manual review recommended) **Integration Example:** ``` User drops 3 new nodes on canvas ↓ Workflow calls CanvasReasonerActivity ↓ LLM analyzes: Clone → Analyze → Report ↓ Returns edges + reasoning ↓ Frontend updates canvas with suggested connections ↓ User approves/rejects suggestions ``` --- ### Error Handling If LLM inference fails: - First activity retry (2-second backoff) - Second activity retry (4-second backoff) - Third activity retry (8-second backoff) - If all retries fail, workflow records error and proceeds to next activity (or fails if terminal) **Common Failure Scenarios:** - Network timeout: `connection refused` (retry automatically) - Model not found: `unknown model: xyz` (terminal error) - Rate limited: HTTP 429 (retry with exponential backoff) - Prompt too long: `context length exceeded` (terminal error) --- ### Performance & Cost - Single prompt inference: ~100-500ms (model-dependent) - Batch processing: Serial (not parallel), ~100-500ms per prompt - Model inference costs: Free (on-premise Ollama/Reasoning models) - Token counting: Provided in response for quota tracking **Optimization Tips:** - Use `ornith:13b` or `qwen2.5:3b` for faster inference - Use `reasoning` only for complex analysis that needs reasoning - Cache frequently-used prompts at workflow level - Use batch activity for multiple similar prompts (better throughput)