{"openapi":"3.1.0","info":{"title":"Rylvo Public API","version":"1.0.0","description":"The public Rylvo REST surface: run an agentic bot turn and read/write the knowledge base with an org API key. Configuration (bots, prompts, guardrails, flows, connectors, …) is managed in the dashboard or through the MCP endpoint at POST /mcp, not through this API.","contact":{"name":"Rylvo Engineering","url":"https://rylvo.com"}},"servers":[{"url":"https://rylvo.com","description":"Rylvo production"}],"security":[{"bearerApiKey":[]},{"apiKeyHeader":[]},{"rylvoKeyHeader":[]}],"tags":[{"name":"Runtime","description":"Execute bot turns."},{"name":"Knowledge Base","description":"Query and maintain KB documents."}],"components":{"securitySchemes":{"bearerApiKey":{"type":"http","scheme":"bearer","description":"Org API key, `rylv_<40 hex>`, created in Dashboard → API Keys. Scopes: `respond`, `kb:read`, `kb:write`."},"apiKeyHeader":{"type":"apiKey","in":"header","name":"X-API-Key"},"rylvoKeyHeader":{"type":"apiKey","in":"header","name":"X-Rylvo-Key"}},"schemas":{"Error":{"type":"object","required":["error","code"],"properties":{"error":{"type":"string","description":"Human-readable failure message."},"code":{"type":"string","description":"Stable machine-readable error code."}}},"Message":{"type":"object","required":["role","content"],"properties":{"role":{"type":"string","enum":["user","assistant"]},"content":{"type":"string"}}},"BotRunRequest":{"type":"object","required":["message"],"description":"Exactly one of `botId` or `groupId` is required.","properties":{"botId":{"type":"string","description":"Bot id from Dashboard → Bots."},"groupId":{"type":"string","description":"Agent-group id. Wins if both are sent."},"message":{"type":"string","description":"The end user's current message."},"conversationHistory":{"type":"array","items":{"$ref":"#/components/schemas/Message"},"description":"Prior turns. Omit it for an identified end user and the server replays the session's own transcript."},"sessionId":{"type":["string","null"],"description":"Session hint. Honored only alongside `endUserExternalId`; the server may return a fresh id — always send back the `sessionId` from the last response."},"endUserExternalId":{"type":["string","null"],"maxLength":256,"description":"Stable end-user id in your namespace. Required for server-side session memory and for the two fields below."},"endUserDisplayName":{"type":["string","null"],"maxLength":200},"endUserEmail":{"type":["string","null"],"format":"email","maxLength":320},"channel":{"type":"string","enum":["api","widget","unknown"],"default":"api","description":"Operator surfaces (`test`, `dashboard`) and deployed-channel identities (`whatsapp`, `slack`, …) are set server-side and are rejected here."},"model":{"type":["string","null"],"description":"Model override for this turn."},"maxTokens":{"type":"integer"},"temperature":{"type":"number"},"disableTools":{"type":"boolean"},"maxToolIterations":{"type":"integer"},"responseFormat":{"type":"object","description":"Structured output: {\"type\":\"json_object\"} or {\"type\":\"json_schema\", …}."},"stream":{"type":"boolean","description":"Stream the reply as Server-Sent Events. Single-bot only; `Idempotency-Key` is ignored."}}},"BotRunResponse":{"type":"object","required":["response","runId","model"],"properties":{"response":{"type":"string","description":"The assistant's reply."},"conversationId":{"type":"string","description":"The effective session id, or an empty string when the turn ran without an identified end user."},"sessionId":{"type":["string","null"],"description":"Session id to send on the next turn; null when no session was resolved."},"sessionNew":{"type":"boolean"},"runId":{"type":"string","description":"Trace id — find it in Dashboard → Traces."},"model":{"type":"string"},"latencyMs":{"type":"number"},"tokens":{"type":"object","properties":{"prompt":{"type":"integer"},"completion":{"type":"integer"},"total":{"type":"integer"}}},"guardrails":{"type":"object","properties":{"inputBlocked":{"type":"boolean"},"outputRewritten":{"type":"boolean"},"blockedBy":{"type":["string","null"]},"escalated":{"type":"boolean"},"escalatedBy":{"type":["string","null"]}}},"toolCalls":{"type":"array","items":{"type":"object"}},"augmentation":{"type":"object","properties":{"rulesApplied":{"type":"integer"},"skillsMatched":{"type":"integer"},"episodicRecallCount":{"type":"integer"},"userModelApplied":{"type":"boolean"}}},"collectionEvents":{"type":"array","items":{"type":"object"},"description":"Data-collection writes attempted this turn."},"collectionState":{"type":"object","description":"Slot-filling state after this turn."},"sessionEnd":{"type":["object","null"]},"kb":{"type":["object","null"],"description":"KB grounding telemetry and citations."},"pendingApproval":{"type":["object","null"]}}},"KbQueryRequest":{"type":"object","required":["connectionId","query"],"properties":{"connectionId":{"type":"string"},"query":{"type":"string"},"topK":{"type":"integer","minimum":1,"maximum":100,"default":5},"filters":{"type":"object","description":"Optional retrieval filters. Supported keys: `sourceId`, `documentId`. Any other key is rejected with 400.","properties":{"sourceId":{"type":"string"},"documentId":{"type":"string"}},"additionalProperties":false}}},"KbQueryResponse":{"type":"object","properties":{"query":{"type":"string"},"connectionId":{"type":"string"},"results":{"type":"array","items":{"type":"object","properties":{"chunkId":{"type":"string"},"content":{"type":"string"},"score":{"type":"number"},"metadata":{"type":"object"}}}}}},"KbDocumentsRequest":{"type":"object","required":["sourceId","connectionId","documents"],"properties":{"sourceId":{"type":"string"},"connectionId":{"type":"string"},"documents":{"type":"array","minItems":1,"maxItems":100,"items":{"type":"object","required":["name","content"],"properties":{"id":{"type":"string","description":"Existing document id — supply it to update in place."},"name":{"type":"string"},"content":{"type":"string"},"mimeType":{"type":"string","default":"text/plain"},"metadata":{"type":"object"}}}}}},"KbDocumentsResponse":{"type":"object","properties":{"success":{"type":"boolean"},"results":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["created","updated","error"]},"error":{"type":"string"}}}}}}}},"paths":{"/api/v1/bot-run":{"post":{"tags":["Runtime"],"operationId":"runBotTurn","summary":"Run one bot or agent-group turn","description":"Executes a full agentic turn on your own LLM key (BYOK): prompt assembly, guardrails, tools/connectors, KB retrieval, skills, reply. Requires the `respond` scope. Rate limited per API key (60/min by default, overridable per key); responses carry `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset`.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"},"description":"Stripe-style replay. Re-sending the same key returns the recorded response with `Idempotent-Replay: true`. Ignored when `stream` is true."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BotRunRequest"}}}},"responses":{"200":{"description":"The turn result. When `stream` is true the body is `text/event-stream` instead, ending with a `result` event carrying this same object.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BotRunResponse"}}}},"400":{"description":"Malformed JSON or an invalid field.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or unrecognized API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Key revoked, out of scope, or the org is suspended.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`bot_not_found` / `group_not_found` — no such bot or agent group in this org.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`bot_not_runnable` — the bot exists but is draft or archived.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — see `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"`engine_unreachable` — the runtime engine could not be reached.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`feature_disabled` / `engine_not_ready`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/kb/query":{"post":{"tags":["Knowledge Base"],"operationId":"queryKnowledgeBase","summary":"Run retrieval against a knowledge-base connection","description":"Requires the `kb:read` scope. Rate limited per API key (60/min by default).","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KbQueryRequest"}}}},"responses":{"200":{"description":"Ranked chunks.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KbQueryResponse"}}}},"400":{"description":"Malformed JSON or an invalid field.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or unrecognized API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Key revoked, out of scope, or the org is suspended.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found` — no such connection in this org.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — see `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`vector_store_unavailable`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/kb/documents":{"post":{"tags":["Knowledge Base"],"operationId":"pushKnowledgeBaseDocuments","summary":"Push documents into a KB source (max 100 per request)","description":"Each document is chunked, embedded and indexed synchronously, so it is queryable when the call returns. Requires the `kb:write` scope. Rate limited per API key (20/min by default).","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KbDocumentsRequest"}}}},"responses":{"200":{"description":"Per-document outcome. A document that failed to index is reported with `status: \"error\"` inside a 200 body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KbDocumentsResponse"}}}},"400":{"description":"Malformed JSON or an invalid field.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or unrecognized API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Key revoked, out of scope, or the org is suspended.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found` — no such source or connection in this org.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — see `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Knowledge Base"],"operationId":"deleteKnowledgeBaseDocument","summary":"Remove one KB document","description":"Deletes the document's vector chunks and stored bytes and soft-deletes its record. Requires the `kb:write` scope.","parameters":[{"name":"documentId","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Deleted.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"documentId":{"type":"string"}}}}}},"400":{"description":"Malformed JSON or an invalid field.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or unrecognized API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Key revoked, out of scope, or the org is suspended.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found` — no such document in this org.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — see `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}