{
  "openapi": "3.1.0",
  "info": {
    "title": "Opportunity Radar Agent API",
    "version": "1.0.0",
    "description": "Read the materialized research index and publish immutable, schema-validated research bundles and evidence artifacts."
  },
  "servers": [{ "url": "/" }],
  "paths": {
    "/api/v1/health": {
      "get": {
        "summary": "Check service and journal health",
        "operationId": "getHealth",
        "responses": {
          "200": {
            "description": "Healthy",
            "content": { "application/json": { "schema": { "type": "object" } } }
          }
        }
      }
    },
    "/api/v1/schema": {
      "get": {
        "summary": "Get the strict research bundle JSON Schema",
        "operationId": "getResearchBundleSchema",
        "responses": {
          "200": {
            "description": "JSON Schema for bundle writes",
            "content": { "application/schema+json": { "schema": { "type": "object" } } }
          }
        }
      }
    },
    "/api/v1/snapshot": {
      "get": {
        "summary": "Get the current materialized research index",
        "operationId": "getSnapshot",
        "responses": {
          "200": {
            "description": "Current topics, runs, papers, experiments, opportunities, reports, and sources",
            "content": { "application/json": { "schema": { "type": "object" } } }
          }
        }
      }
    },
    "/api/v1/bundles": {
      "post": {
        "summary": "Commit an atomic research bundle",
        "description": "The bundleId is the idempotency key. Reusing it with identical content is a successful duplicate; reusing it with different content is a conflict.",
        "operationId": "commitBundle",
        "security": [{ "writeToken": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "/api/v1/schema" }
            }
          }
        },
        "responses": {
          "201": { "description": "Bundle committed" },
          "200": { "description": "Identical retry already committed" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "409": { "$ref": "#/components/responses/Conflict" },
          "422": { "$ref": "#/components/responses/Invalid" }
        }
      }
    },
    "/api/v1/resources/{kind}/{id}": {
      "parameters": [
        { "$ref": "#/components/parameters/Kind" },
        { "$ref": "#/components/parameters/Id" }
      ],
      "get": {
        "summary": "Get one resource",
        "operationId": "getResource",
        "responses": {
          "200": {
            "description": "Resource entity",
            "content": { "application/json": { "schema": { "type": "object" } } }
          },
          "404": { "description": "Resource not found" }
        }
      },
      "put": {
        "summary": "Upsert one resource through an atomic bundle",
        "operationId": "upsertResource",
        "security": [{ "writeToken": [] }],
        "parameters": [
          { "$ref": "#/components/parameters/IdempotencyKey" },
          { "$ref": "#/components/parameters/GeneratedAt" },
          { "$ref": "#/components/parameters/AgentName" },
          { "$ref": "#/components/parameters/AgentSkill" }
        ],
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "type": "object" } } }
        },
        "responses": {
          "201": { "description": "Resource committed" },
          "200": { "description": "Identical retry already committed" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "409": { "$ref": "#/components/responses/Conflict" },
          "422": { "$ref": "#/components/responses/Invalid" }
        }
      },
      "delete": {
        "summary": "Delete one resource through an atomic bundle",
        "operationId": "deleteResource",
        "security": [{ "writeToken": [] }],
        "parameters": [
          { "$ref": "#/components/parameters/IdempotencyKey" },
          { "$ref": "#/components/parameters/GeneratedAt" },
          { "$ref": "#/components/parameters/AgentName" },
          { "$ref": "#/components/parameters/AgentSkill" }
        ],
        "responses": {
          "201": { "description": "Deletion committed" },
          "200": { "description": "Identical retry already committed" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "409": { "$ref": "#/components/responses/Conflict" },
          "422": { "$ref": "#/components/responses/Invalid" }
        }
      }
    },
    "/api/v1/artifacts/{artifactPath}": {
      "parameters": [
        {
          "name": "artifactPath",
          "in": "path",
          "required": true,
          "description": "Two to ten safe path segments, normally run/subject/experiment/file",
          "schema": { "type": "string" },
          "allowReserved": true
        }
      ],
      "get": {
        "summary": "Read an immutable evidence artifact",
        "operationId": "getArtifact",
        "responses": {
          "200": { "description": "Artifact bytes" },
          "404": { "description": "Artifact not found" }
        }
      },
      "put": {
        "summary": "Publish an immutable evidence artifact",
        "description": "An identical retry succeeds. Different bytes at an existing path are rejected.",
        "operationId": "putArtifact",
        "security": [{ "writeToken": [] }],
        "requestBody": {
          "required": true,
          "content": { "application/octet-stream": { "schema": { "type": "string", "format": "binary" } } }
        },
        "responses": {
          "201": { "description": "Artifact created" },
          "200": { "description": "Identical artifact already exists" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "409": { "$ref": "#/components/responses/Conflict" },
          "413": { "description": "Artifact exceeds the upload limit" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "writeToken": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "opaque"
      }
    },
    "parameters": {
      "Kind": {
        "name": "kind",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "enum": ["topics", "runs", "papers", "experiments", "opportunities", "reports", "sources"]
        }
      },
      "Id": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": { "type": "string" }
      },
      "IdempotencyKey": {
        "name": "X-Idempotency-Key",
        "in": "header",
        "required": false,
        "description": "Stable key for safe retries",
        "schema": { "type": "string" }
      },
      "GeneratedAt": {
        "name": "X-Generated-At",
        "in": "header",
        "required": false,
        "description": "Stable ISO 8601 timestamp to pair with an idempotency key",
        "schema": { "type": "string", "format": "date-time" }
      },
      "AgentName": {
        "name": "X-Agent-Name",
        "in": "header",
        "required": false,
        "schema": { "type": "string" }
      },
      "AgentSkill": {
        "name": "X-Agent-Skill",
        "in": "header",
        "required": false,
        "schema": { "type": "string" }
      }
    },
    "responses": {
      "Unauthorized": { "description": "Missing or invalid write token" },
      "Conflict": { "description": "Idempotency or immutable-path conflict" },
      "Invalid": { "description": "Schema or relationship validation failed" }
    }
  }
}
