{
  "openapi": "3.1.0",
  "info": {
    "title": "Karmen Native API",
    "version": "1.0.0",
    "description": "Karmen's native server-to-server contract. Each API key activates one deployed Karmen AI instance whose identity, knowledge, memory and authorized capabilities are configured in the Karmen workbench."
  },
  "servers": [
    {
      "url": "https://your-karmen-api.example",
      "description": "Replace with the HTTPS API origin shown by the Karmen workbench"
    }
  ],
  "tags": [
    {"name": "Connection", "description": "Credential and Karmen instance configuration checks that do not invoke an AI response"},
    {"name": "Chat", "description": "Conversation with the Karmen instance bound to the API key"}
  ],
  "paths": {
    "/v1/connection": {
      "get": {
        "operationId": "getConnection",
        "summary": "Check the API key and bound Karmen instance",
        "description": "Checks authentication and configuration only. It does not invoke the model and therefore always reports chat.verified as false.",
        "tags": ["Connection"],
        "security": [{"bearerAuth": []}],
        "responses": {
          "200": {
            "description": "The key is valid and its Karmen instance binding was found",
            "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Connection"}}}
          },
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "403": {"$ref": "#/components/responses/ServerOnly"},
          "429": {"$ref": "#/components/responses/RateLimited"},
          "503": {"$ref": "#/components/responses/ServiceUnavailable"}
        }
      }
    },
    "/v1/chat": {
      "post": {
        "operationId": "createJobReply",
        "summary": "Send one message to the bound Karmen instance",
        "description": "The bearer key activates one deployed Karmen instance. requestId provides body-level idempotency within the same instance and user scope. Persist the complete original JSON request before sending and preserve its field values and property order when retrying; the current implementation compares JSON.stringify of the chat body after removing userId. A completed matching attempt may return its cached result. A timeout or upstream error does not prove that execution did not occur. For request_outcome_uncertain, stop automatic retries and ask the administrator to investigate; do not generate a new business requestId to bypass the uncertainty. There is no public request lookup or reconciliation endpoint.",
        "tags": ["Chat"],
        "security": [{"bearerAuth": []}],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": ["requestId", "userId", "message"],
                "properties": {
                  "requestId": {"type": "string", "minLength": 16, "maxLength": 100, "pattern": "^[A-Za-z0-9_-]{16,100}$", "description": "Stable business identifier. Persist it with the complete original request before sending. This is not the diagnostic requestId returned in an error response."},
                  "userId": {"type": "string", "minLength": 1, "maxLength": 200, "description": "Stable authenticated user identity from the caller's server"},
                  "message": {"type": "string", "minLength": 1, "maxLength": 20000, "description": "Complete business message, limited to 20,000 Unicode characters. The JSON request body is limited to 262,144 bytes; the message is not truncated before inference."},
                  "conversationId": {"type": "string", "minLength": 16, "maxLength": 100, "pattern": "^[A-Za-z0-9_-]{16,100}$", "description": "Previously returned conversation owned by the same user and job. Omit this field when starting a new conversation."}
                }
              },
              "examples": {
                "firstMessage": {"value": {"requestId": "req_0123456789abcdef0123456789abcdef", "userId": "crm-user-1842", "message": "请介绍这项服务。"}},
                "continueConversation": {"value": {"requestId": "req_fedcba9876543210fedcba9876543210", "userId": "crm-user-1842", "message": "请继续说明合作流程。", "conversationId": "conv_0123456789abcdef0123456789abcdef"}}
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A Karmen instance reply was persisted",
            "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ChatResponse"}}}
          },
          "400": {"$ref": "#/components/responses/BadRequest"},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "403": {"$ref": "#/components/responses/ServerOnly"},
          "404": {"$ref": "#/components/responses/NotFound"},
          "409": {"$ref": "#/components/responses/Conflict"},
          "413": {"$ref": "#/components/responses/RequestTooLarge"},
          "415": {"$ref": "#/components/responses/JSONRequired"},
          "429": {"$ref": "#/components/responses/RateLimited"},
          "502": {"$ref": "#/components/responses/UpstreamUnavailable"},
          "503": {"$ref": "#/components/responses/ServiceUnavailable"}
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {"type": "http", "scheme": "bearer", "bearerFormat": "Karmen server API key", "description": "A server-only key issued for one deployed Karmen instance. Never embed it in browser or mobile client code."}
    },
    "schemas": {
      "Connection": {
        "type": "object",
        "additionalProperties": false,
        "required": ["authenticated", "apiVersion", "jobId", "status", "chat"],
        "properties": {
          "authenticated": {"const": true},
          "apiVersion": {"const": "v1"},
          "jobId": {"type": "string", "pattern": "^job-[a-z0-9]{32}$"},
          "status": {"type": "string", "enum": ["draft", "active", "paused"]},
          "chat": {
            "type": "object",
            "additionalProperties": false,
            "required": ["configured", "enabled", "verified"],
            "properties": {
              "configured": {"type": "boolean"},
              "enabled": {"type": "boolean"},
              "verified": {"const": false, "description": "This endpoint never invokes the model"}
            }
          }
        }
      },
      "ChatResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": ["reply", "conversationId", "sources"],
        "properties": {
          "reply": {"type": "string"},
          "conversationId": {"type": "string"},
          "sources": {"type": "array", "items": {"$ref": "#/components/schemas/Source"}}
        }
      },
      "Source": {
        "type": "object",
        "additionalProperties": false,
        "required": ["label", "documentId", "name", "section"],
        "properties": {
          "label": {"type": "string"},
          "documentId": {"type": "string"},
          "name": {"type": "string"},
          "section": {"type": "string"}
        }
      },
      "Error": {
        "type": "object",
        "additionalProperties": false,
        "required": ["error", "requestId"],
        "properties": {
          "error": {"type": "string", "pattern": "^[a-z][a-z0-9_]{0,79}$"},
          "requestId": {"type": "string", "pattern": "^req_[A-Za-z0-9_-]{16,100}$", "description": "A newly generated diagnostic trace identifier for this error, not an echo of the business identifier. Keep the original body.requestId and saved request separately; never use this diagnostic identifier for a business retry."}
        }
      }
    },
    "responses": {
      "BadRequest": {"description": "Invalid request", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}},
      "Unauthorized": {"description": "Invalid, expired, deleted, or revoked API key", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}},
      "ServerOnly": {"description": "Server API keys are rejected in browser-originated requests", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}},
      "NotFound": {"description": "Resource not found in this job and user scope", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}},
      "Conflict": {"description": "Paused job or unresolved business attempt. request_id_conflict: the same business requestId was supplied with different chat content or property order. request_in_progress: the prior attempt is still pending. request_outcome_uncertain: the prior outcome is unknown; stop automatic retries and ask the administrator to investigate without generating a replacement business requestId.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}},
      "RequestTooLarge": {"description": "request_too_large: the JSON request body exceeds 262144 bytes. This byte limit is separate from the message character limit.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}, "example": {"error": "request_too_large", "requestId": "req_0123456789abcdef"}}}},
      "JSONRequired": {"description": "json_required: Content-Type must be application/json, optionally with parameters such as charset=utf-8.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}, "example": {"error": "json_required", "requestId": "req_0123456789abcdef"}}}},
      "RateLimited": {"description": "Rate or concurrency limit exceeded", "headers": {"Retry-After": {"schema": {"type": "integer", "minimum": 1}}}, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}},
      "UpstreamUnavailable": {"description": "No successful upstream result was returned. Execution may already have occurred; this response does not prove that nothing ran. Keep the original business request and do not generate a replacement requestId to retry an uncertain outcome.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}},
      "ServiceUnavailable": {"description": "The job or its model service is not configured", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}}
    }
  }
}
