{
  "openapi": "3.1.0",
  "info": {
    "title": "Smara Memory API",
    "description": "\nPersistent, multi-tenant memory for AI agents.\n\n**Four cognitive pillars**\n\n* **Working** \u2014 ephemeral per-session scratchpad and a bounded recent-turns\n  window (older turns are compressed into a rolling summary).\n* **Semantic** \u2014 durable facts plus an entity knowledge graph, both bi-temporal:\n  nothing is overwritten, facts are superseded and every change is recorded in\n  the revision history.\n* **Episodic** \u2014 an append-only log of what happened, searchable by vector or\n  keyword, and the trigger source for background consolidation.\n* **Procedural** \u2014 a versioned skill registry with a human-in-the-loop approval\n  gate (draft -> proposed -> approved -> deprecated).\n\n`GET /v1/context` composes all four into a single token-budgeted payload for one\nagent turn.\n\n**Authentication**\n\n* Data-plane routes take an API key: `Authorization: Bearer smara_live_...`.\n  The key resolves to a tenant, and that tenant is the isolation boundary \u2014 a\n  caller can never widen its own scope.\n* Control-plane routes (`/v1/tenants*`) take a Firebase ID token from the dev\n  portal and additionally require membership of the tenant in the path.\n",
    "contact": {
      "name": "Prajana AI",
      "email": "support@prajana.ai"
    },
    "version": "0.1.0"
  },
  "paths": {
    "/health": {
      "get": {
        "tags": [
          "health"
        ],
        "summary": "Liveness probe",
        "description": "Unauthenticated liveness check for Cloud Run. Reports only that the process is up \u2014 it deliberately does not call Spanner, Valkey or Vertex, so a slow dependency cannot cause a restart loop.",
        "operationId": "health_health_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HealthResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/context": {
      "get": {
        "tags": [
          "context"
        ],
        "summary": "Assemble working-memory context for one agent turn",
        "description": "Runs the hybrid retrieval pipeline and returns a token-budgeted context payload.\n\n1. Embeds `query` and links it to knowledge-graph entities (name match + embedding similarity).\n2. Retrieves in parallel-equivalent legs: semantic facts (ANN), episodic events (ANN), and a 1-3 hop graph traversal from the linked entities.\n3. Re-ranks by similarity, recency, importance and hop distance.\n4. Packs sections into `token_budget`, dropping the lowest-ranked items first. Unused budget carries forward to the next section.\n\nOnly currently-valid facts are returned unless `include_historical` is set. If a single retrieval leg fails the call still succeeds with that section empty and its name listed in `degraded_sections`.\n\nRequires the `memory:read` scope.",
        "operationId": "get_context_v1_context_get",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "query",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "description": "What the agent needs context for \u2014 the user's turn or a sub-goal.",
              "title": "Query"
            },
            "description": "What the agent needs context for \u2014 the user's turn or a sub-goal."
          },
          {
            "name": "session_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Session whose scratchpad and recent turns should be included.",
              "title": "Session Id"
            },
            "description": "Session whose scratchpad and recent turns should be included."
          },
          {
            "name": "token_budget",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "integer",
                  "exclusiveMinimum": 0
                },
                {
                  "type": "null"
                }
              ],
              "description": "Total token budget for the payload. Defaults to the service setting.",
              "title": "Token Budget"
            },
            "description": "Total token budget for the payload. Defaults to the service setting."
          },
          {
            "name": "include_historical",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "description": "Include superseded/expired facts and edges (temporal queries).",
              "default": false,
              "title": "Include Historical"
            },
            "description": "Include superseded/expired facts and edges (temporal queries)."
          },
          {
            "name": "system_prompt",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Override the default system prompt.",
              "title": "System Prompt"
            },
            "description": "Override the default system prompt."
          },
          {
            "name": "user_id",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Restrict to memories owned by this end user.",
              "title": "User Id"
            },
            "description": "Restrict to memories owned by this end user."
          },
          {
            "name": "agent_id",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Restrict to memories owned by this agent.",
              "title": "Agent Id"
            },
            "description": "Restrict to memories owned by this agent."
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkingMemoryContext"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted (missing scope or tenant membership).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/v1/sessions/{session_id}/events": {
      "post": {
        "tags": [
          "sessions"
        ],
        "summary": "Capture an episodic event",
        "description": "Appends an event to the immutable episodic log, embedding its summary (or content) for later semantic search.\n\nAfter the write, consolidation heuristics are evaluated: an explicit `session_end`, an `importance` above the configured threshold, or every Nth event in the session enqueues the session for the consolidation worker. Requires the `memory:write` scope.",
        "operationId": "capture_event_v1_sessions__session_id__events_post",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "session_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Caller-defined session identifier.",
              "title": "Session Id"
            },
            "description": "Caller-defined session identifier."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EventCaptureRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EventCaptureResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted (missing scope or tenant membership).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/v1/sessions/{session_id}/scratchpad": {
      "get": {
        "tags": [
          "sessions"
        ],
        "summary": "Read the session scratchpad",
        "description": "Returns the ephemeral scratchpad (goal, plan, findings, ...). Requires the `memory:read` scope.",
        "operationId": "get_scratchpad_v1_sessions__session_id__scratchpad_get",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "session_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Caller-defined session identifier.",
              "title": "Session Id"
            },
            "description": "Caller-defined session identifier."
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScratchpadResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted (missing scope or tenant membership).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "sessions"
        ],
        "summary": "Replace the session scratchpad",
        "description": "Overwrites the whole scratchpad. Requires the `memory:write` scope.",
        "operationId": "put_scratchpad_v1_sessions__session_id__scratchpad_put",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "session_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Caller-defined session identifier.",
              "title": "Session Id"
            },
            "description": "Caller-defined session identifier."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Scratchpad"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScratchpadResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted (missing scope or tenant membership).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "sessions"
        ],
        "summary": "Merge into the session scratchpad",
        "description": "Shallow-merges the supplied keys into the existing scratchpad, creating it if absent. Keys you omit are untouched. Requires the `memory:write` scope.",
        "operationId": "patch_scratchpad_v1_sessions__session_id__scratchpad_patch",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "session_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Caller-defined session identifier.",
              "title": "Session Id"
            },
            "description": "Caller-defined session identifier."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ScratchpadPatch"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScratchpadResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted (missing scope or tenant membership).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/v1/sessions/{session_id}/turns": {
      "post": {
        "tags": [
          "sessions"
        ],
        "summary": "Append a conversational turn",
        "description": "Adds a turn to the recent-turns window. Once the session exceeds the configured window size, turns that age out are compressed into the scratchpad's `rolling_summary` so history stays bounded. Requires the `memory:write` scope.",
        "operationId": "append_turn_v1_sessions__session_id__turns_post",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "session_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Caller-defined session identifier.",
              "title": "Session Id"
            },
            "description": "Caller-defined session identifier."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TurnAppendRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TurnAppendResult"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted (missing scope or tenant membership).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/v1/semantic/search": {
      "get": {
        "tags": [
          "semantic"
        ],
        "summary": "Vector search over semantic facts",
        "description": "Embeds `q` and runs an approximate nearest-neighbour search over `semantic_facts`, scoped to the API key's tenant. Only currently-valid facts are returned unless `include_historical` is set. Requires the `memory:read` scope.",
        "operationId": "search_facts_v1_semantic_search_get",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Natural-language query.",
              "title": "Q"
            },
            "description": "Natural-language query."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "maximum": 200,
              "exclusiveMinimum": 0,
              "description": "Maximum facts to return.",
              "default": 20,
              "title": "Limit"
            },
            "description": "Maximum facts to return."
          },
          {
            "name": "include_historical",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "description": "Include superseded/expired facts.",
              "default": false,
              "title": "Include Historical"
            },
            "description": "Include superseded/expired facts."
          },
          {
            "name": "min_confidence",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "number",
                  "maximum": 1.0,
                  "minimum": 0.0
                },
                {
                  "type": "null"
                }
              ],
              "description": "Drop facts below this confidence.",
              "title": "Min Confidence"
            },
            "description": "Drop facts below this confidence."
          },
          {
            "name": "topics",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                {
                  "type": "null"
                }
              ],
              "description": "Restrict to facts tagged with any of these topics.",
              "title": "Topics"
            },
            "description": "Restrict to facts tagged with any of these topics."
          },
          {
            "name": "user_id",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Restrict to memories owned by this end user.",
              "title": "User Id"
            },
            "description": "Restrict to memories owned by this end user."
          },
          {
            "name": "agent_id",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Restrict to memories owned by this agent.",
              "title": "Agent Id"
            },
            "description": "Restrict to memories owned by this agent."
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SemanticSearchResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted (missing scope or tenant membership).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/v1/semantic/facts": {
      "post": {
        "tags": [
          "semantic"
        ],
        "summary": "Write a semantic fact",
        "description": "Embeds and persists a fact. If `supersedes` is set, each listed fact is closed out (`valid_to` = this fact's `valid_from`) in the same logical operation, and both the creation and the supersessions are written to `revision_history`. Requires the `memory:write` scope.",
        "operationId": "create_fact_v1_semantic_facts_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FactCreateRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FactResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted (missing scope or tenant membership).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/v1/episodic/search": {
      "get": {
        "tags": [
          "episodic"
        ],
        "summary": "Search episodic events",
        "description": "Two modes:\n\n* `vector` (default) \u2014 embeds `q` and runs approximate nearest-neighbour search over `episodic_events`, returning a `distance` per hit.\n* `keyword` \u2014 case-insensitive substring match over event content and summary, newest first, with no `distance`.\n\nRequires the `memory:read` scope.",
        "operationId": "search_events_v1_episodic_search_get",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Query text.",
              "title": "Q"
            },
            "description": "Query text."
          },
          {
            "name": "mode",
            "in": "query",
            "required": false,
            "schema": {
              "enum": [
                "vector",
                "keyword"
              ],
              "type": "string",
              "description": "Retrieval strategy.",
              "default": "vector",
              "title": "Mode"
            },
            "description": "Retrieval strategy."
          },
          {
            "name": "session_id",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Restrict the search to a single session.",
              "title": "Session Id"
            },
            "description": "Restrict the search to a single session."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "maximum": 200,
              "exclusiveMinimum": 0,
              "description": "Maximum events to return.",
              "default": 20,
              "title": "Limit"
            },
            "description": "Maximum events to return."
          },
          {
            "name": "user_id",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Restrict to memories owned by this end user.",
              "title": "User Id"
            },
            "description": "Restrict to memories owned by this end user."
          },
          {
            "name": "agent_id",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Restrict to memories owned by this agent.",
              "title": "Agent Id"
            },
            "description": "Restrict to memories owned by this agent."
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EpisodicSearchResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted (missing scope or tenant membership).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/v1/episodic/sessions/{session_id}/events": {
      "get": {
        "tags": [
          "episodic"
        ],
        "summary": "List a session's episodic events",
        "description": "Chronological replay of one session's event log. Requires the `memory:read` scope.",
        "operationId": "list_session_events_v1_episodic_sessions__session_id__events_get",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "session_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Session Id"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "maximum": 1000,
              "exclusiveMinimum": 0,
              "default": 100,
              "title": "Limit"
            }
          },
          {
            "name": "user_id",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Restrict to memories owned by this end user.",
              "title": "User Id"
            },
            "description": "Restrict to memories owned by this end user."
          },
          {
            "name": "agent_id",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Restrict to memories owned by this agent.",
              "title": "Agent Id"
            },
            "description": "Restrict to memories owned by this agent."
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SessionEventsResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted (missing scope or tenant membership).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/v1/procedural/skills": {
      "get": {
        "tags": [
          "procedural"
        ],
        "summary": "List skills",
        "description": "Lists the tenant's skills, optionally filtered by status. Requires the `memory:read` scope.",
        "operationId": "list_skills_v1_procedural_skills_get",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "$ref": "#/components/schemas/SkillStatus"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Filter by lifecycle status.",
              "title": "Status"
            },
            "description": "Filter by lifecycle status."
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SkillListResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted (missing scope or tenant membership).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "procedural"
        ],
        "summary": "Create a skill",
        "description": "Creates a skill in `draft` and writes v1 of its body to GCS. New skills are never active: they must be proposed and then approved. Requires the `memory:write` scope.",
        "operationId": "create_skill_v1_procedural_skills_post",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SkillCreateRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SkillResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted (missing scope or tenant membership).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/v1/procedural/skills/{skill_id}": {
      "get": {
        "tags": [
          "procedural"
        ],
        "summary": "Get a skill",
        "description": "Fetches one skill, optionally with its Markdown body. Requires the `memory:read` scope.",
        "operationId": "get_skill_v1_procedural_skills__skill_id__get",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "skill_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Skill identifier.",
              "title": "Skill Id"
            },
            "description": "Skill identifier."
          },
          {
            "name": "include_body",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "description": "Also fetch the body from GCS.",
              "default": false,
              "title": "Include Body"
            },
            "description": "Also fetch the body from GCS."
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SkillResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted (missing scope or tenant membership).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Resource does not exist in this tenant.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "procedural"
        ],
        "summary": "Update a skill",
        "description": "Patches metadata and/or the body. A body change bumps `version` and writes a new GCS object; if the skill was `approved` it drops back to `draft` (and `approved_by` is cleared) so the new body is re-reviewed. Status is not settable here \u2014 use the propose/approve routes. Requires the `memory:write` scope.",
        "operationId": "update_skill_v1_procedural_skills__skill_id__patch",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "skill_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Skill identifier.",
              "title": "Skill Id"
            },
            "description": "Skill identifier."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SkillUpdateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SkillResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted (missing scope or tenant membership).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Resource does not exist in this tenant.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "The requested state change is not allowed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/v1/procedural/skills/{skill_id}/propose": {
      "post": {
        "tags": [
          "procedural"
        ],
        "summary": "Propose a skill for review",
        "description": "`draft` -> `proposed`. Any other starting status is a 409. Requires the `memory:write` scope.",
        "operationId": "propose_skill_v1_procedural_skills__skill_id__propose_post",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "skill_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Skill identifier.",
              "title": "Skill Id"
            },
            "description": "Skill identifier."
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProceduralSkill"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted (missing scope or tenant membership).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Resource does not exist in this tenant.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "The requested state change is not allowed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/v1/procedural/skills/{skill_id}/approve": {
      "post": {
        "tags": [
          "procedural"
        ],
        "summary": "Approve a proposed skill",
        "description": "`proposed` -> `approved`. Only approved skills are surfaced by `GET /v1/context`. Approving anything that is not currently `proposed` is a 409 \u2014 a draft can never be approved directly. Requires the `memory:write` scope.",
        "operationId": "approve_skill_v1_procedural_skills__skill_id__approve_post",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "skill_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Skill identifier.",
              "title": "Skill Id"
            },
            "description": "Skill identifier."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SkillApproveRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProceduralSkill"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted (missing scope or tenant membership).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Resource does not exist in this tenant.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "The requested state change is not allowed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/v1/procedural/skills/{skill_id}/deprecate": {
      "post": {
        "tags": [
          "procedural"
        ],
        "summary": "Deprecate a skill",
        "description": "`draft`|`approved` -> `deprecated` (terminal). Requires the `memory:write` scope.",
        "operationId": "deprecate_skill_v1_procedural_skills__skill_id__deprecate_post",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "skill_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Skill identifier.",
              "title": "Skill Id"
            },
            "description": "Skill identifier."
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProceduralSkill"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted (missing scope or tenant membership).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Resource does not exist in this tenant.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "The requested state change is not allowed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/v1/consolidate/trigger": {
      "post": {
        "tags": [
          "consolidation"
        ],
        "summary": "Trigger session consolidation",
        "description": "Publishes a session-ready message to Pub/Sub, enqueuing the session for the consolidation worker (fact extraction, graph updates, conflict resolution). Use this for testing or forced re-processing \u2014 normal operation triggers automatically on session end, on a high-importance event, or every Nth event.\n\nReturns 202: the work happens asynchronously. Requires the `memory:write` scope.",
        "operationId": "trigger_consolidation_v1_consolidate_trigger_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConsolidateTriggerRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConsolidateTriggerResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted (missing scope or tenant membership).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/v1/tenants": {
      "get": {
        "tags": [
          "control-plane"
        ],
        "summary": "List my tenants",
        "description": "Every tenant the authenticated Firebase user belongs to, with their role in each. The entry point for a returning portal user: there is no other way to discover a tenant_id from just a Firebase session.",
        "operationId": "list_my_tenants_v1_tenants_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TenantListResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted (missing scope or tenant membership).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "FirebaseIdToken": []
          }
        ]
      },
      "post": {
        "tags": [
          "control-plane"
        ],
        "summary": "Create a tenant",
        "description": "Provisions a tenant and makes the authenticated portal user its `owner` in the same operation. Authenticated with a Firebase ID token.",
        "operationId": "create_tenant_v1_tenants_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TenantCreateRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TenantResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted (missing scope or tenant membership).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        },
        "security": [
          {
            "FirebaseIdToken": []
          }
        ]
      }
    },
    "/v1/tenants/{tenant_id}": {
      "get": {
        "tags": [
          "control-plane"
        ],
        "summary": "Get a tenant",
        "description": "Returns the tenant, the caller's role, and the full member list (the only route that includes members \u2014 the portal's tenant detail page is the one place that needs them). Members only (403 otherwise).",
        "operationId": "get_tenant_v1_tenants__tenant_id__get",
        "security": [
          {
            "FirebaseIdToken": []
          }
        ],
        "parameters": [
          {
            "name": "tenant_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Tenant the request targets.",
              "title": "Tenant Id"
            },
            "description": "Tenant the request targets."
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TenantResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted (missing scope or tenant membership).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Resource does not exist in this tenant.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/v1/tenants/{tenant_id}/api-keys": {
      "post": {
        "tags": [
          "control-plane"
        ],
        "summary": "Issue an API key",
        "description": "Mints a data-plane API key for this tenant and returns the raw secret **once**. Only the SHA-256 hash and a display prefix are persisted. Members only.",
        "operationId": "issue_api_key_v1_tenants__tenant_id__api_keys_post",
        "security": [
          {
            "FirebaseIdToken": []
          }
        ],
        "parameters": [
          {
            "name": "tenant_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Tenant the request targets.",
              "title": "Tenant Id"
            },
            "description": "Tenant the request targets."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ApiKeyCreateRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IssuedApiKeyResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted (missing scope or tenant membership).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "control-plane"
        ],
        "summary": "List API keys",
        "description": "Lists this tenant's keys, newest first. Returns metadata only \u2014 never the secret or its hash. Members only.",
        "operationId": "list_api_keys_v1_tenants__tenant_id__api_keys_get",
        "security": [
          {
            "FirebaseIdToken": []
          }
        ],
        "parameters": [
          {
            "name": "tenant_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Tenant the request targets.",
              "title": "Tenant Id"
            },
            "description": "Tenant the request targets."
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKeyListResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted (missing scope or tenant membership).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/v1/tenants/{tenant_id}/api-keys/{key_id}/revoke": {
      "post": {
        "tags": [
          "control-plane"
        ],
        "summary": "Revoke an API key",
        "description": "Marks the key `revoked`; every subsequent data-plane call with it gets a 401. Idempotent, and the row is retained for audit. Members only.",
        "operationId": "revoke_api_key_v1_tenants__tenant_id__api_keys__key_id__revoke_post",
        "security": [
          {
            "FirebaseIdToken": []
          }
        ],
        "parameters": [
          {
            "name": "tenant_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Tenant the request targets.",
              "title": "Tenant Id"
            },
            "description": "Tenant the request targets."
          },
          {
            "name": "key_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "API key identifier.",
              "title": "Key Id"
            },
            "description": "API key identifier."
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKeySummary"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted (missing scope or tenant membership).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Resource does not exist in this tenant.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/": {
      "get": {
        "tags": [
          "meta"
        ],
        "summary": "Service information",
        "description": "Unauthenticated service metadata and links to the API docs.",
        "operationId": "root__get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceInfo"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "ApiKeyCreateRequest": {
        "properties": {
          "name": {
            "type": "string",
            "title": "Name",
            "description": "Label to identify the key later, e.g. 'staging-agent'."
          },
          "scopes": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "title": "Scopes",
            "description": "Permissions granted, e.g. ['memory:read', 'memory:write']."
          },
          "expires_at": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "title": "Expires At",
            "description": "Optional expiry. Omit for a non-expiring key."
          }
        },
        "type": "object",
        "required": [
          "name"
        ],
        "title": "ApiKeyCreateRequest"
      },
      "ApiKeyListResponse": {
        "properties": {
          "count": {
            "type": "integer",
            "title": "Count"
          },
          "api_keys": {
            "items": {
              "$ref": "#/components/schemas/ApiKeySummary"
            },
            "type": "array",
            "title": "Api Keys",
            "description": "Metadata only; secrets are never returned."
          }
        },
        "type": "object",
        "required": [
          "count",
          "api_keys"
        ],
        "title": "ApiKeyListResponse"
      },
      "ApiKeyStatus": {
        "type": "string",
        "enum": [
          "active",
          "revoked"
        ],
        "title": "ApiKeyStatus"
      },
      "ApiKeySummary": {
        "properties": {
          "id": {
            "type": "string",
            "title": "Id"
          },
          "tenant_id": {
            "type": "string",
            "title": "Tenant Id"
          },
          "name": {
            "type": "string",
            "title": "Name"
          },
          "key_prefix": {
            "type": "string",
            "title": "Key Prefix",
            "description": "Display prefix, e.g. 'smara_live_8f2a3c12'."
          },
          "scopes": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "title": "Scopes"
          },
          "status": {
            "$ref": "#/components/schemas/ApiKeyStatus"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "title": "Created At"
          },
          "last_used_at": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "title": "Last Used At"
          },
          "expires_at": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "title": "Expires At"
          },
          "created_by": {
            "type": "string",
            "title": "Created By",
            "description": "Firebase uid of the portal user who issued the key."
          }
        },
        "type": "object",
        "required": [
          "id",
          "tenant_id",
          "name",
          "key_prefix",
          "scopes",
          "status",
          "created_at",
          "created_by"
        ],
        "title": "ApiKeySummary",
        "description": "API key metadata. Never includes the secret or its hash."
      },
      "BudgetReport": {
        "properties": {
          "token_budget": {
            "type": "integer",
            "title": "Token Budget"
          },
          "estimated_tokens_used": {
            "type": "integer",
            "title": "Estimated Tokens Used",
            "default": 0
          },
          "sections": {
            "additionalProperties": {
              "$ref": "#/components/schemas/SectionBudget"
            },
            "type": "object",
            "title": "Sections"
          }
        },
        "type": "object",
        "required": [
          "token_budget"
        ],
        "title": "BudgetReport"
      },
      "ConsolidateTriggerRequest": {
        "properties": {
          "user_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "User Id",
            "description": "End user this record belongs to."
          },
          "agent_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Agent Id",
            "description": "Agent this record belongs to."
          },
          "session_id": {
            "type": "string",
            "title": "Session Id",
            "description": "Session to hand to the consolidation worker."
          },
          "reason": {
            "type": "string",
            "title": "Reason",
            "description": "Recorded on the Pub/Sub message for traceability.",
            "default": "manual"
          }
        },
        "type": "object",
        "required": [
          "session_id"
        ],
        "title": "ConsolidateTriggerRequest"
      },
      "ConsolidateTriggerResponse": {
        "properties": {
          "session_id": {
            "type": "string",
            "title": "Session Id"
          },
          "reason": {
            "type": "string",
            "title": "Reason"
          },
          "published": {
            "type": "boolean",
            "title": "Published",
            "description": "False if the publish failed; the call still returns 202."
          },
          "message_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Message Id"
          }
        },
        "type": "object",
        "required": [
          "session_id",
          "reason",
          "published"
        ],
        "title": "ConsolidateTriggerResponse"
      },
      "EpisodicSearchResponse": {
        "properties": {
          "query": {
            "type": "string",
            "title": "Query"
          },
          "mode": {
            "type": "string",
            "enum": [
              "vector",
              "keyword"
            ],
            "title": "Mode"
          },
          "count": {
            "type": "integer",
            "title": "Count"
          },
          "results": {
            "items": {
              "$ref": "#/components/schemas/EventHitResponse"
            },
            "type": "array",
            "title": "Results"
          }
        },
        "type": "object",
        "required": [
          "query",
          "mode",
          "count",
          "results"
        ],
        "title": "EpisodicSearchResponse"
      },
      "ErrorResponse": {
        "properties": {
          "code": {
            "type": "string",
            "title": "Code",
            "description": "Stable machine-readable error code."
          },
          "message": {
            "type": "string",
            "title": "Message",
            "description": "Human-readable explanation."
          },
          "detail": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "title": "Detail",
            "description": "Optional structured context."
          }
        },
        "type": "object",
        "required": [
          "code",
          "message"
        ],
        "title": "ErrorResponse",
        "description": "Uniform error envelope returned by every non-2xx response.",
        "examples": [
          {
            "code": "not_found",
            "message": "Skill 'skill_abc' not found"
          }
        ]
      },
      "EventCaptureRequest": {
        "properties": {
          "user_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "User Id",
            "description": "End user this record belongs to."
          },
          "agent_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Agent Id",
            "description": "Agent this record belongs to."
          },
          "type": {
            "$ref": "#/components/schemas/EventType",
            "description": "tool_call | decision | outcome | user_statement"
          },
          "content": {
            "type": "string",
            "title": "Content",
            "description": "Full text of the event."
          },
          "summary": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Summary",
            "description": "Short form. When present it is what gets embedded and retrieved."
          },
          "importance": {
            "type": "number",
            "maximum": 1.0,
            "minimum": 0.0,
            "title": "Importance",
            "description": "Drives retrieval ranking, and triggers consolidation above the threshold.",
            "default": 0.5
          },
          "timestamp": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "title": "Timestamp",
            "description": "Defaults to the time the event is received."
          },
          "derived_facts": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "title": "Derived Facts",
            "description": "Ids of semantic facts already derived from this event."
          },
          "session_end": {
            "type": "boolean",
            "title": "Session End",
            "description": "Signals the session is over; always triggers consolidation.",
            "default": false
          }
        },
        "type": "object",
        "required": [
          "type",
          "content"
        ],
        "title": "EventCaptureRequest",
        "description": "One thing that happened in a session."
      },
      "EventCaptureResponse": {
        "properties": {
          "event": {
            "$ref": "#/components/schemas/EventResponse"
          },
          "session_event_count": {
            "type": "integer",
            "title": "Session Event Count",
            "description": "Events recorded for this session so far."
          },
          "consolidation_triggered": {
            "type": "boolean",
            "title": "Consolidation Triggered",
            "description": "Whether a consolidation message was published to Pub/Sub."
          },
          "consolidation_reason": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Consolidation Reason",
            "description": "'session_end', 'high_importance' or 'event_count'."
          },
          "consolidation_message_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Consolidation Message Id"
          }
        },
        "type": "object",
        "required": [
          "event",
          "session_event_count",
          "consolidation_triggered"
        ],
        "title": "EventCaptureResponse"
      },
      "EventHitResponse": {
        "properties": {
          "event": {
            "$ref": "#/components/schemas/EventResponse"
          },
          "distance": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Distance",
            "description": "Set for vector search; null for keyword search."
          }
        },
        "type": "object",
        "required": [
          "event"
        ],
        "title": "EventHitResponse"
      },
      "EventResponse": {
        "properties": {
          "id": {
            "type": "string",
            "title": "Id"
          },
          "session_id": {
            "type": "string",
            "title": "Session Id"
          },
          "scope": {
            "$ref": "#/components/schemas/Scope"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time",
            "title": "Timestamp"
          },
          "type": {
            "$ref": "#/components/schemas/EventType"
          },
          "content": {
            "type": "string",
            "title": "Content"
          },
          "summary": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Summary"
          },
          "importance": {
            "type": "number",
            "title": "Importance"
          },
          "derived_facts": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "title": "Derived Facts",
            "description": "Ids of semantic facts derived from this event."
          }
        },
        "type": "object",
        "required": [
          "id",
          "session_id",
          "scope",
          "timestamp",
          "type",
          "content",
          "importance"
        ],
        "title": "EventResponse",
        "description": "An episodic event as returned by the API (embedding omitted)."
      },
      "EventType": {
        "type": "string",
        "enum": [
          "tool_call",
          "decision",
          "outcome",
          "user_statement"
        ],
        "title": "EventType"
      },
      "FactCreateRequest": {
        "properties": {
          "user_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "User Id",
            "description": "End user this record belongs to."
          },
          "agent_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Agent Id",
            "description": "Agent this record belongs to."
          },
          "content": {
            "type": "string",
            "title": "Content",
            "description": "The fact, as a self-contained sentence."
          },
          "entities": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "title": "Entities",
            "description": "Ids of graph nodes this fact refers to."
          },
          "topics": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "title": "Topics",
            "description": "Free-form topic tags."
          },
          "confidence": {
            "type": "number",
            "maximum": 1.0,
            "minimum": 0.0,
            "title": "Confidence",
            "description": "Belief in this fact.",
            "default": 0.8
          },
          "importance": {
            "type": "number",
            "maximum": 1.0,
            "minimum": 0.0,
            "title": "Importance",
            "description": "Retrieval priority.",
            "default": 0.5
          },
          "valid_from": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "title": "Valid From",
            "description": "When the fact became true. Defaults to now."
          },
          "supersedes": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "title": "Supersedes",
            "description": "Ids of facts this one replaces. Each is expired (`valid_to` set) and a supersede revision is recorded."
          },
          "source_sessions": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "title": "Source Sessions",
            "description": "Sessions this fact was derived from."
          },
          "extraction_model": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Extraction Model",
            "description": "Model that produced the fact, e.g. 'gemini-2.5-flash'."
          }
        },
        "type": "object",
        "required": [
          "content"
        ],
        "title": "FactCreateRequest",
        "description": "A durable, tenant-scoped statement of fact."
      },
      "FactHitResponse": {
        "properties": {
          "fact": {
            "$ref": "#/components/schemas/FactResponse"
          },
          "distance": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Distance",
            "description": "Approximate cosine distance; lower is more similar."
          }
        },
        "type": "object",
        "required": [
          "fact"
        ],
        "title": "FactHitResponse"
      },
      "FactResponse": {
        "properties": {
          "id": {
            "type": "string",
            "title": "Id"
          },
          "scope": {
            "$ref": "#/components/schemas/Scope"
          },
          "content": {
            "type": "string",
            "title": "Content"
          },
          "entities": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "title": "Entities",
            "description": "Ids of graph nodes this fact mentions."
          },
          "topics": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "title": "Topics"
          },
          "confidence": {
            "type": "number",
            "title": "Confidence"
          },
          "importance": {
            "type": "number",
            "title": "Importance"
          },
          "valid_from": {
            "type": "string",
            "format": "date-time",
            "title": "Valid From"
          },
          "valid_to": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "title": "Valid To",
            "description": "Null while the fact is current; set when superseded or expired."
          },
          "supersedes": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "title": "Supersedes"
          },
          "provenance": {
            "$ref": "#/components/schemas/Provenance"
          }
        },
        "type": "object",
        "required": [
          "id",
          "scope",
          "content",
          "confidence",
          "importance",
          "valid_from",
          "provenance"
        ],
        "title": "FactResponse",
        "description": "A semantic fact as returned by the API (embedding omitted)."
      },
      "GraphNodeRef": {
        "properties": {
          "id": {
            "type": "string",
            "title": "Id"
          },
          "name": {
            "type": "string",
            "title": "Name"
          },
          "type": {
            "type": "string",
            "title": "Type"
          }
        },
        "type": "object",
        "required": [
          "id",
          "name",
          "type"
        ],
        "title": "GraphNodeRef"
      },
      "HTTPValidationError": {
        "properties": {
          "detail": {
            "items": {
              "$ref": "#/components/schemas/ValidationError"
            },
            "type": "array",
            "title": "Detail"
          }
        },
        "type": "object",
        "title": "HTTPValidationError"
      },
      "HealthResponse": {
        "properties": {
          "status": {
            "type": "string",
            "title": "Status",
            "description": "'ok' when the process is serving traffic."
          },
          "service": {
            "type": "string",
            "title": "Service"
          },
          "version": {
            "type": "string",
            "title": "Version"
          },
          "environment": {
            "type": "string",
            "title": "Environment"
          }
        },
        "type": "object",
        "required": [
          "status",
          "service",
          "version",
          "environment"
        ],
        "title": "HealthResponse"
      },
      "IssuedApiKeyResponse": {
        "properties": {
          "api_key": {
            "$ref": "#/components/schemas/ApiKeySummary"
          },
          "secret": {
            "type": "string",
            "title": "Secret",
            "description": "The raw API key. Shown exactly once \u2014 only its SHA-256 hash is stored, so it cannot be retrieved again. Lose it and you must revoke and re-issue."
          }
        },
        "type": "object",
        "required": [
          "api_key",
          "secret"
        ],
        "title": "IssuedApiKeyResponse",
        "description": "The only response that ever contains the secret."
      },
      "LinkedEntity": {
        "properties": {
          "node_id": {
            "type": "string",
            "title": "Node Id"
          },
          "name": {
            "type": "string",
            "title": "Name"
          },
          "type": {
            "type": "string",
            "title": "Type"
          },
          "match": {
            "type": "string",
            "enum": [
              "name",
              "embedding"
            ],
            "title": "Match",
            "description": "'name' = substring/alias match, 'embedding' = ANN."
          },
          "distance": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Distance",
            "description": "Vector distance, for embedding matches only."
          }
        },
        "type": "object",
        "required": [
          "node_id",
          "name",
          "type",
          "match"
        ],
        "title": "LinkedEntity",
        "description": "A graph node the query was linked to, and how it was matched."
      },
      "MemberRole": {
        "type": "string",
        "enum": [
          "owner",
          "member"
        ],
        "title": "MemberRole"
      },
      "ProceduralSkill": {
        "properties": {
          "id": {
            "type": "string",
            "title": "Id"
          },
          "scope": {
            "$ref": "#/components/schemas/Scope"
          },
          "name": {
            "type": "string",
            "title": "Name"
          },
          "version": {
            "type": "integer",
            "title": "Version",
            "default": 1
          },
          "status": {
            "$ref": "#/components/schemas/SkillStatus",
            "default": "draft"
          },
          "trigger_conditions": {
            "type": "string",
            "title": "Trigger Conditions"
          },
          "gcs_path": {
            "type": "string",
            "title": "Gcs Path"
          },
          "success_metrics": {
            "additionalProperties": {
              "type": "number"
            },
            "type": "object",
            "title": "Success Metrics"
          },
          "created_by": {
            "type": "string",
            "title": "Created By"
          },
          "approved_by": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Approved By"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "title": "Created At"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "title": "Updated At"
          }
        },
        "type": "object",
        "required": [
          "scope",
          "name",
          "trigger_conditions",
          "gcs_path",
          "created_by",
          "created_at",
          "updated_at"
        ],
        "title": "ProceduralSkill"
      },
      "Provenance": {
        "properties": {
          "source_sessions": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "title": "Source Sessions"
          },
          "extraction_model": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Extraction Model"
          },
          "extracted_at": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "title": "Extracted At"
          },
          "actor": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Actor"
          }
        },
        "type": "object",
        "title": "Provenance"
      },
      "RetrievedEvent": {
        "properties": {
          "id": {
            "type": "string",
            "title": "Id"
          },
          "session_id": {
            "type": "string",
            "title": "Session Id"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time",
            "title": "Timestamp"
          },
          "type": {
            "type": "string",
            "title": "Type"
          },
          "text": {
            "type": "string",
            "title": "Text",
            "description": "The event summary when present, otherwise its raw content."
          },
          "importance": {
            "type": "number",
            "title": "Importance"
          },
          "distance": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Distance"
          },
          "score": {
            "type": "number",
            "title": "Score"
          },
          "estimated_tokens": {
            "type": "integer",
            "title": "Estimated Tokens"
          }
        },
        "type": "object",
        "required": [
          "id",
          "session_id",
          "timestamp",
          "type",
          "text",
          "importance",
          "score",
          "estimated_tokens"
        ],
        "title": "RetrievedEvent"
      },
      "RetrievedFact": {
        "properties": {
          "id": {
            "type": "string",
            "title": "Id"
          },
          "content": {
            "type": "string",
            "title": "Content"
          },
          "entities": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "title": "Entities"
          },
          "topics": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "title": "Topics"
          },
          "confidence": {
            "type": "number",
            "title": "Confidence"
          },
          "importance": {
            "type": "number",
            "title": "Importance"
          },
          "valid_from": {
            "type": "string",
            "format": "date-time",
            "title": "Valid From"
          },
          "distance": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Distance"
          },
          "score": {
            "type": "number",
            "title": "Score",
            "description": "Re-ranker score in [0, 1]; higher is better."
          },
          "estimated_tokens": {
            "type": "integer",
            "title": "Estimated Tokens"
          }
        },
        "type": "object",
        "required": [
          "id",
          "content",
          "confidence",
          "importance",
          "valid_from",
          "score",
          "estimated_tokens"
        ],
        "title": "RetrievedFact"
      },
      "RetrievedGraphPath": {
        "properties": {
          "hop_count": {
            "type": "integer",
            "title": "Hop Count"
          },
          "nodes": {
            "items": {
              "$ref": "#/components/schemas/GraphNodeRef"
            },
            "type": "array",
            "title": "Nodes"
          },
          "relations": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "title": "Relations"
          },
          "rendered": {
            "type": "string",
            "title": "Rendered",
            "description": "Human/LLM-readable one-line rendering of the path."
          },
          "seed_distance": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Seed Distance"
          },
          "score": {
            "type": "number",
            "title": "Score"
          },
          "estimated_tokens": {
            "type": "integer",
            "title": "Estimated Tokens"
          }
        },
        "type": "object",
        "required": [
          "hop_count",
          "nodes",
          "relations",
          "rendered",
          "score",
          "estimated_tokens"
        ],
        "title": "RetrievedGraphPath"
      },
      "RetrievedSkill": {
        "properties": {
          "id": {
            "type": "string",
            "title": "Id"
          },
          "name": {
            "type": "string",
            "title": "Name"
          },
          "version": {
            "type": "integer",
            "title": "Version"
          },
          "status": {
            "$ref": "#/components/schemas/SkillStatus"
          },
          "trigger_conditions": {
            "type": "string",
            "title": "Trigger Conditions"
          },
          "gcs_path": {
            "type": "string",
            "title": "Gcs Path",
            "description": "Where the full Markdown body lives; fetch on demand."
          },
          "score": {
            "type": "number",
            "title": "Score"
          },
          "estimated_tokens": {
            "type": "integer",
            "title": "Estimated Tokens"
          }
        },
        "type": "object",
        "required": [
          "id",
          "name",
          "version",
          "status",
          "trigger_conditions",
          "gcs_path",
          "score",
          "estimated_tokens"
        ],
        "title": "RetrievedSkill"
      },
      "Scope": {
        "properties": {
          "tenant_id": {
            "type": "string",
            "title": "Tenant Id"
          },
          "user_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "User Id"
          },
          "agent_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Agent Id"
          }
        },
        "type": "object",
        "required": [
          "tenant_id"
        ],
        "title": "Scope"
      },
      "Scratchpad": {
        "properties": {
          "goal": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Goal",
            "description": "What the agent is trying to achieve."
          },
          "plan": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "title": "Plan",
            "description": "Ordered steps toward the goal."
          },
          "current_step": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Current Step",
            "description": "The step in progress."
          },
          "findings": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "title": "Findings",
            "description": "What has been learned so far."
          },
          "open_questions": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "title": "Open Questions",
            "description": "Unresolved questions blocking progress."
          },
          "confidence": {
            "anyOf": [
              {
                "type": "number",
                "maximum": 1.0,
                "minimum": 0.0
              },
              {
                "type": "null"
              }
            ],
            "title": "Confidence",
            "description": "Agent's confidence in the current plan."
          },
          "rolling_summary": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Rolling Summary",
            "description": "Compressed summary of turns that have aged out of the recent-turns window. Maintained by the service, not by callers."
          },
          "summarized_turn_count": {
            "type": "integer",
            "minimum": 0.0,
            "title": "Summarized Turn Count",
            "description": "Bookkeeping: how many of the session's turns are already folded into `rolling_summary`. Maintained by the service.",
            "default": 0
          }
        },
        "additionalProperties": true,
        "type": "object",
        "title": "Scratchpad",
        "description": "Structured per-session agent state.\n\nExtra keys are preserved (``extra=\"allow\"``) so callers can carry their own\ntask-specific state without a schema change on our side."
      },
      "ScratchpadPatch": {
        "properties": {
          "goal": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Goal"
          },
          "plan": {
            "anyOf": [
              {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "title": "Plan"
          },
          "current_step": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Current Step"
          },
          "findings": {
            "anyOf": [
              {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "title": "Findings"
          },
          "open_questions": {
            "anyOf": [
              {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "title": "Open Questions"
          },
          "confidence": {
            "anyOf": [
              {
                "type": "number",
                "maximum": 1.0,
                "minimum": 0.0
              },
              {
                "type": "null"
              }
            ],
            "title": "Confidence"
          }
        },
        "additionalProperties": true,
        "type": "object",
        "title": "ScratchpadPatch",
        "description": "Partial scratchpad update. Only the keys you send are changed."
      },
      "ScratchpadResponse": {
        "properties": {
          "session_id": {
            "type": "string",
            "title": "Session Id"
          },
          "scratchpad": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Scratchpad"
              },
              {
                "type": "null"
              }
            ],
            "description": "Null if nothing has been written for this session yet."
          },
          "turn_count": {
            "type": "integer",
            "title": "Turn Count",
            "description": "Turns recorded for this session."
          }
        },
        "type": "object",
        "required": [
          "session_id",
          "turn_count"
        ],
        "title": "ScratchpadResponse"
      },
      "SectionBudget": {
        "properties": {
          "allocated": {
            "type": "integer",
            "title": "Allocated",
            "description": "Tokens allotted, including carry-over from earlier sections."
          },
          "used": {
            "type": "integer",
            "title": "Used",
            "description": "Estimated tokens actually packed.",
            "default": 0
          },
          "kept": {
            "type": "integer",
            "title": "Kept",
            "description": "Items included.",
            "default": 0
          },
          "dropped": {
            "type": "integer",
            "title": "Dropped",
            "description": "Items truncated for lack of budget.",
            "default": 0
          }
        },
        "type": "object",
        "required": [
          "allocated"
        ],
        "title": "SectionBudget",
        "description": "Per-section accounting, surfaced in the API response for observability."
      },
      "SemanticSearchResponse": {
        "properties": {
          "query": {
            "type": "string",
            "title": "Query"
          },
          "count": {
            "type": "integer",
            "title": "Count"
          },
          "results": {
            "items": {
              "$ref": "#/components/schemas/FactHitResponse"
            },
            "type": "array",
            "title": "Results"
          }
        },
        "type": "object",
        "required": [
          "query",
          "count",
          "results"
        ],
        "title": "SemanticSearchResponse"
      },
      "ServiceInfo": {
        "properties": {
          "service": {
            "type": "string",
            "title": "Service"
          },
          "version": {
            "type": "string",
            "title": "Version"
          },
          "environment": {
            "type": "string",
            "title": "Environment"
          },
          "docs_url": {
            "type": "string",
            "title": "Docs Url",
            "description": "Interactive API documentation."
          },
          "openapi_url": {
            "type": "string",
            "title": "Openapi Url",
            "description": "Machine-readable OpenAPI specification."
          }
        },
        "type": "object",
        "required": [
          "service",
          "version",
          "environment",
          "docs_url",
          "openapi_url"
        ],
        "title": "ServiceInfo"
      },
      "SessionEventsResponse": {
        "properties": {
          "session_id": {
            "type": "string",
            "title": "Session Id"
          },
          "count": {
            "type": "integer",
            "title": "Count"
          },
          "events": {
            "items": {
              "$ref": "#/components/schemas/EventResponse"
            },
            "type": "array",
            "title": "Events",
            "description": "Events in chronological order."
          }
        },
        "type": "object",
        "required": [
          "session_id",
          "count",
          "events"
        ],
        "title": "SessionEventsResponse"
      },
      "SkillApproveRequest": {
        "properties": {
          "approved_by": {
            "type": "string",
            "title": "Approved By",
            "description": "Identity of the human approving the skill."
          }
        },
        "type": "object",
        "required": [
          "approved_by"
        ],
        "title": "SkillApproveRequest"
      },
      "SkillCreateRequest": {
        "properties": {
          "user_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "User Id",
            "description": "End user this record belongs to."
          },
          "agent_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Agent Id",
            "description": "Agent this record belongs to."
          },
          "name": {
            "type": "string",
            "title": "Name",
            "description": "Human-readable skill name."
          },
          "trigger_conditions": {
            "type": "string",
            "title": "Trigger Conditions",
            "description": "When this skill applies, e.g. 'user expresses frustration -> escalate'."
          },
          "body_markdown": {
            "type": "string",
            "title": "Body Markdown",
            "description": "The skill body, stored in GCS at `skills/{tenant}/{id}/v1.md`.",
            "default": ""
          },
          "created_by": {
            "type": "string",
            "title": "Created By",
            "description": "Who authored it (agent id, service or human)."
          },
          "success_metrics": {
            "additionalProperties": {
              "type": "number"
            },
            "type": "object",
            "title": "Success Metrics",
            "description": "Named metrics, e.g. {'resolution_rate': 0.82}."
          }
        },
        "type": "object",
        "required": [
          "name",
          "trigger_conditions",
          "created_by"
        ],
        "title": "SkillCreateRequest"
      },
      "SkillListResponse": {
        "properties": {
          "count": {
            "type": "integer",
            "title": "Count"
          },
          "skills": {
            "items": {
              "$ref": "#/components/schemas/ProceduralSkill"
            },
            "type": "array",
            "title": "Skills"
          }
        },
        "type": "object",
        "required": [
          "count",
          "skills"
        ],
        "title": "SkillListResponse"
      },
      "SkillResponse": {
        "properties": {
          "skill": {
            "$ref": "#/components/schemas/ProceduralSkill"
          },
          "body": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Body",
            "description": "Markdown body from GCS; null unless requested."
          },
          "generation": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Generation",
            "description": "GCS generation written by this request, when it wrote one."
          }
        },
        "type": "object",
        "required": [
          "skill"
        ],
        "title": "SkillResponse",
        "description": "Skill metadata, plus the Markdown body when it was requested."
      },
      "SkillStatus": {
        "type": "string",
        "enum": [
          "draft",
          "proposed",
          "approved",
          "deprecated"
        ],
        "title": "SkillStatus"
      },
      "SkillUpdateRequest": {
        "properties": {
          "name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Name"
          },
          "trigger_conditions": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Trigger Conditions"
          },
          "success_metrics": {
            "anyOf": [
              {
                "additionalProperties": {
                  "type": "number"
                },
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "title": "Success Metrics"
          },
          "body_markdown": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Body Markdown",
            "description": "New body. Bumps `version` and writes a new GCS object; if the skill was approved it returns to `draft` for re-review."
          }
        },
        "type": "object",
        "title": "SkillUpdateRequest",
        "description": "Partial update. Supplying `body_markdown` forks a new version."
      },
      "Tenant": {
        "properties": {
          "id": {
            "type": "string",
            "title": "Id"
          },
          "name": {
            "type": "string",
            "title": "Name"
          },
          "type": {
            "$ref": "#/components/schemas/TenantType",
            "default": "internal"
          },
          "status": {
            "$ref": "#/components/schemas/TenantStatus",
            "default": "active"
          },
          "plan": {
            "type": "string",
            "title": "Plan",
            "default": "internal"
          },
          "owner_email": {
            "type": "string",
            "title": "Owner Email"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "title": "Created At"
          }
        },
        "type": "object",
        "required": [
          "name",
          "owner_email",
          "created_at"
        ],
        "title": "Tenant"
      },
      "TenantCreateRequest": {
        "properties": {
          "name": {
            "type": "string",
            "title": "Name",
            "description": "Display name for the tenant."
          },
          "type": {
            "$ref": "#/components/schemas/TenantType",
            "description": "internal (first-party) or external (customer).",
            "default": "internal"
          },
          "plan": {
            "type": "string",
            "title": "Plan",
            "description": "Billing plan identifier (stub for now).",
            "default": "internal"
          },
          "owner_email": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Owner Email",
            "description": "Owner contact. Defaults to the email on the authenticated Firebase token."
          }
        },
        "type": "object",
        "required": [
          "name"
        ],
        "title": "TenantCreateRequest"
      },
      "TenantListResponse": {
        "properties": {
          "tenants": {
            "items": {
              "$ref": "#/components/schemas/TenantResponse"
            },
            "type": "array",
            "title": "Tenants",
            "description": "Every tenant the authenticated user belongs to, with their role in each."
          }
        },
        "type": "object",
        "required": [
          "tenants"
        ],
        "title": "TenantListResponse"
      },
      "TenantMember": {
        "properties": {
          "tenant_id": {
            "type": "string",
            "title": "Tenant Id"
          },
          "firebase_uid": {
            "type": "string",
            "title": "Firebase Uid"
          },
          "role": {
            "$ref": "#/components/schemas/MemberRole",
            "default": "member"
          },
          "joined_at": {
            "type": "string",
            "format": "date-time",
            "title": "Joined At"
          }
        },
        "type": "object",
        "required": [
          "tenant_id",
          "firebase_uid",
          "joined_at"
        ],
        "title": "TenantMember"
      },
      "TenantResponse": {
        "properties": {
          "tenant": {
            "$ref": "#/components/schemas/Tenant"
          },
          "role": {
            "$ref": "#/components/schemas/MemberRole",
            "description": "The calling user's role in this tenant."
          },
          "members": {
            "anyOf": [
              {
                "items": {
                  "$ref": "#/components/schemas/TenantMember"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "title": "Members",
            "description": "Populated on the single-tenant detail fetch (GET /tenants/{id}) only. Omitted on create/list-mine to avoid an N+1 members lookup per tenant where no UI needs it."
          }
        },
        "type": "object",
        "required": [
          "tenant",
          "role"
        ],
        "title": "TenantResponse"
      },
      "TenantStatus": {
        "type": "string",
        "enum": [
          "active",
          "suspended"
        ],
        "title": "TenantStatus"
      },
      "TenantType": {
        "type": "string",
        "enum": [
          "internal",
          "external"
        ],
        "title": "TenantType"
      },
      "Turn": {
        "properties": {
          "role": {
            "type": "string",
            "title": "Role",
            "description": "Speaker role, e.g. 'user', 'assistant', 'tool'."
          },
          "content": {
            "type": "string",
            "title": "Content",
            "description": "Turn text."
          },
          "timestamp": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "title": "Timestamp",
            "description": "When the turn happened."
          },
          "metadata": {
            "additionalProperties": true,
            "type": "object",
            "title": "Metadata"
          }
        },
        "type": "object",
        "required": [
          "role",
          "content"
        ],
        "title": "Turn",
        "description": "One conversational turn held in the recent-turns window."
      },
      "TurnAppendRequest": {
        "properties": {
          "role": {
            "type": "string",
            "title": "Role",
            "description": "Speaker role, e.g. 'user', 'assistant', 'tool'."
          },
          "content": {
            "type": "string",
            "title": "Content"
          },
          "timestamp": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "title": "Timestamp"
          },
          "metadata": {
            "additionalProperties": true,
            "type": "object",
            "title": "Metadata"
          }
        },
        "type": "object",
        "required": [
          "role",
          "content"
        ],
        "title": "TurnAppendRequest"
      },
      "TurnAppendResult": {
        "properties": {
          "turn": {
            "$ref": "#/components/schemas/Turn"
          },
          "turn_count": {
            "type": "integer",
            "title": "Turn Count",
            "description": "Total turns recorded for the session."
          },
          "compressed": {
            "type": "boolean",
            "title": "Compressed",
            "description": "True if aged-out turns were folded into the rolling summary.",
            "default": false
          },
          "rolling_summary": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Rolling Summary"
          }
        },
        "type": "object",
        "required": [
          "turn",
          "turn_count"
        ],
        "title": "TurnAppendResult",
        "description": "Outcome of appending a turn, including whether compression fired."
      },
      "ValidationError": {
        "properties": {
          "loc": {
            "items": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "integer"
                }
              ]
            },
            "type": "array",
            "title": "Location"
          },
          "msg": {
            "type": "string",
            "title": "Message"
          },
          "type": {
            "type": "string",
            "title": "Error Type"
          },
          "input": {
            "title": "Input"
          },
          "ctx": {
            "type": "object",
            "title": "Context"
          }
        },
        "type": "object",
        "required": [
          "loc",
          "msg",
          "type"
        ],
        "title": "ValidationError"
      },
      "WorkingMemoryContext": {
        "properties": {
          "scope": {
            "$ref": "#/components/schemas/Scope"
          },
          "session_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Session Id"
          },
          "query": {
            "type": "string",
            "title": "Query"
          },
          "generated_at": {
            "type": "string",
            "format": "date-time",
            "title": "Generated At"
          },
          "system_prompt": {
            "type": "string",
            "title": "System Prompt"
          },
          "scratchpad": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "title": "Scratchpad",
            "description": "Session scratchpad, possibly with low-priority keys dropped."
          },
          "recent_turns": {
            "items": {
              "$ref": "#/components/schemas/Turn"
            },
            "type": "array",
            "title": "Recent Turns",
            "description": "Most recent turns, oldest first."
          },
          "semantic_facts": {
            "items": {
              "$ref": "#/components/schemas/RetrievedFact"
            },
            "type": "array",
            "title": "Semantic Facts"
          },
          "graph_paths": {
            "items": {
              "$ref": "#/components/schemas/RetrievedGraphPath"
            },
            "type": "array",
            "title": "Graph Paths"
          },
          "episodic_events": {
            "items": {
              "$ref": "#/components/schemas/RetrievedEvent"
            },
            "type": "array",
            "title": "Episodic Events"
          },
          "procedural_skills": {
            "items": {
              "$ref": "#/components/schemas/RetrievedSkill"
            },
            "type": "array",
            "title": "Procedural Skills"
          },
          "linked_entities": {
            "items": {
              "$ref": "#/components/schemas/LinkedEntity"
            },
            "type": "array",
            "title": "Linked Entities",
            "description": "Graph entities the query was linked to."
          },
          "budget": {
            "$ref": "#/components/schemas/BudgetReport",
            "description": "Per-section token allocation and usage."
          },
          "degraded_sections": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "title": "Degraded Sections",
            "description": "Retrieval legs that failed and were skipped. The response is still usable; these sections are empty rather than the whole call failing."
          }
        },
        "type": "object",
        "required": [
          "scope",
          "query",
          "generated_at",
          "system_prompt",
          "budget"
        ],
        "title": "WorkingMemoryContext",
        "description": "Everything an agent should see for one turn, already budget-truncated."
      }
    },
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "http",
        "description": "Data-plane API key: `Authorization: Bearer smara_live_...`",
        "scheme": "bearer"
      },
      "FirebaseIdToken": {
        "type": "http",
        "description": "Dev-portal Firebase ID token: `Authorization: Bearer <id_token>`",
        "scheme": "bearer"
      }
    }
  },
  "tags": [
    {
      "name": "context",
      "description": "The per-turn hybrid retrieval endpoint."
    },
    {
      "name": "sessions",
      "description": "Episodic capture and working-memory state."
    },
    {
      "name": "semantic",
      "description": "Durable facts and knowledge-graph reads/writes."
    },
    {
      "name": "episodic",
      "description": "Search over the event log."
    },
    {
      "name": "procedural",
      "description": "Skill registry and approval workflow."
    },
    {
      "name": "consolidation",
      "description": "Hand a session to the background worker."
    },
    {
      "name": "control-plane",
      "description": "Tenants, membership and API keys (portal auth)."
    },
    {
      "name": "health",
      "description": "Liveness probe."
    },
    {
      "name": "meta",
      "description": "Service metadata."
    }
  ]
}
