{
  "openapi": "3.1.0",
  "info": {
    "title": "Specky API",
    "version": "1.0.0-beta",
    "summary": "Public HTTP surface for AI agents and integrations.",
    "description": "Specky is an AI-native product workspace. The v1 REST surface mirrors the Specky MCP server (https://specky.space/api/mcp) — every tool reachable over MCP has an HTTP equivalent. Authenticate with a workspace API key (`sk_live_…`) created in Specky → Settings → Developer.",
    "contact": {
      "name": "Specky Support",
      "email": "customer-support@specky.space",
      "url": "https://specky.space/for-agents"
    },
    "license": { "name": "Proprietary", "url": "https://specky.space/terms" }
  },
  "servers": [{ "url": "https://specky.space", "description": "Production" }],
  "security": [{ "bearerAuth": [] }],
  "tags": [
    { "name": "Capture", "description": "Push signals into the Product Graph." },
    { "name": "Workspace", "description": "Read workspace state." },
    { "name": "Documents", "description": "Create and read documents." },
    { "name": "Insights", "description": "Customer/product insights." },
    { "name": "Opportunities", "description": "Opportunity Tree." },
    { "name": "Decisions", "description": "Decision Log." },
    { "name": "Experiments", "description": "Experiment Tracker." },
    { "name": "OKRs", "description": "OKR Tracker." },
    { "name": "Tickets", "description": "Engineering tickets." },
    { "name": "Research", "description": "Alex JTBD interview campaigns." },
    { "name": "Workflows", "description": "Custom workflows." },
    { "name": "Webhooks", "description": "Outbound delivery events." },
    { "name": "Meta", "description": "Discovery and health." }
  ],
  "paths": {
    "/api/v1/search": {
      "get": {
        "tags": ["Workspace"],
        "summary": "Hybrid keyword search across the Product Graph and documents",
        "operationId": "searchWorkspace",
        "parameters": [
          { "name": "q", "in": "query", "required": true, "schema": { "type": "string" } },
          { "name": "source_types", "in": "query", "description": "Comma-separated list of source_type values (SLACK, JIRA, GONG, INSIGHT, …).", "schema": { "type": "string" } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 50, "default": 20 } }
        ],
        "responses": { "200": { "$ref": "#/components/responses/OkSummary" }, "401": { "$ref": "#/components/responses/Unauthorized" } }
      }
    },
    "/api/v1/items/{id}": {
      "get": {
        "tags": ["Workspace"],
        "summary": "Fetch a document or graph node by id or partial name",
        "operationId": "readItem",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string" } },
          { "name": "kind", "in": "query", "schema": { "type": "string", "enum": ["document", "graph_node", "auto"], "default": "auto" } }
        ],
        "responses": { "200": { "$ref": "#/components/responses/OkSummary" }, "401": { "$ref": "#/components/responses/Unauthorized" } }
      }
    },
    "/api/v1/documents": {
      "get": {
        "tags": ["Documents"],
        "summary": "List documents",
        "operationId": "listDocuments",
        "parameters": [
          { "name": "type", "in": "query", "schema": { "type": "string", "enum": ["prd", "spec", "research", "roadmap", "notes", "all"] } },
          { "name": "status", "in": "query", "schema": { "type": "string", "enum": ["draft", "review", "approved", "archived", "all"] } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 20 } }
        ],
        "responses": { "200": { "$ref": "#/components/responses/OkSummary" }, "401": { "$ref": "#/components/responses/Unauthorized" } }
      },
      "post": {
        "tags": ["Documents"],
        "summary": "Create a new document",
        "operationId": "createDocument",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateDocumentRequest" } } } },
        "responses": { "200": { "$ref": "#/components/responses/OkSummary" }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" } }
      }
    },
    "/api/v1/signals": {
      "get": {
        "tags": ["Workspace"],
        "summary": "Recent graph signals",
        "operationId": "listRecentSignals",
        "parameters": [
          { "name": "source_types", "in": "query", "schema": { "type": "string" } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 25 } }
        ],
        "responses": { "200": { "$ref": "#/components/responses/OkSummary" }, "401": { "$ref": "#/components/responses/Unauthorized" } }
      }
    },
    "/api/v1/inbox": {
      "get": {
        "tags": ["Workspace"],
        "summary": "PM Inbox items awaiting review",
        "operationId": "listInbox",
        "parameters": [
          { "name": "status", "in": "query", "schema": { "type": "string", "enum": ["pending", "approved", "dismissed", "all"], "default": "pending" } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 20 } }
        ],
        "responses": { "200": { "$ref": "#/components/responses/OkSummary" }, "401": { "$ref": "#/components/responses/Unauthorized" } }
      }
    },
    "/api/v1/capture": {
      "post": {
        "tags": ["Capture"],
        "summary": "Capture a signal into the Product Graph",
        "operationId": "captureSignal",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CaptureRequest" } } } },
        "responses": { "200": { "$ref": "#/components/responses/OkSummary" }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" } }
      }
    },
    "/api/v1/insights": {
      "post": {
        "tags": ["Insights"],
        "summary": "Record an insight",
        "operationId": "createInsight",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateInsightRequest" } } } },
        "responses": { "200": { "$ref": "#/components/responses/OkSummary" }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" } }
      }
    },
    "/api/v1/opportunities": {
      "post": {
        "tags": ["Opportunities"],
        "summary": "Add an opportunity",
        "operationId": "createOpportunity",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateOpportunityRequest" } } } },
        "responses": { "200": { "$ref": "#/components/responses/OkSummary" }, "401": { "$ref": "#/components/responses/Unauthorized" } }
      }
    },
    "/api/v1/decisions": {
      "post": {
        "tags": ["Decisions"],
        "summary": "Log a decision",
        "operationId": "createDecision",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateDecisionRequest" } } } },
        "responses": { "200": { "$ref": "#/components/responses/OkSummary" }, "401": { "$ref": "#/components/responses/Unauthorized" } }
      }
    },
    "/api/v1/experiments": {
      "post": {
        "tags": ["Experiments"],
        "summary": "Create an experiment",
        "operationId": "createExperiment",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateExperimentRequest" } } } },
        "responses": { "200": { "$ref": "#/components/responses/OkSummary" }, "401": { "$ref": "#/components/responses/Unauthorized" } }
      }
    },
    "/api/v1/okrs": {
      "post": {
        "tags": ["OKRs"],
        "summary": "Create an OKR",
        "operationId": "createOKR",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateOkrRequest" } } } },
        "responses": { "200": { "$ref": "#/components/responses/OkSummary" }, "401": { "$ref": "#/components/responses/Unauthorized" } }
      }
    },
    "/api/v1/tickets": {
      "post": {
        "tags": ["Tickets"],
        "summary": "Persist a batch of engineering tickets",
        "operationId": "createTickets",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateTicketsRequest" } } } },
        "responses": { "200": { "$ref": "#/components/responses/OkSummary" }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" } }
      }
    },
    "/api/v1/research-sessions": {
      "post": {
        "tags": ["Research"],
        "summary": "Start an Alex JTBD research session",
        "operationId": "startResearchSession",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StartResearchRequest" } } } },
        "responses": { "200": { "$ref": "#/components/responses/OkSummary" }, "401": { "$ref": "#/components/responses/Unauthorized" } }
      }
    },
    "/api/v1/workflows": {
      "get": {
        "tags": ["Workflows"],
        "summary": "List custom workflows",
        "operationId": "listWorkflows",
        "parameters": [
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 25 } }
        ],
        "responses": { "200": { "$ref": "#/components/responses/OkSummary" }, "401": { "$ref": "#/components/responses/Unauthorized" } }
      }
    },
    "/api/v1/workflows/{id}/runs": {
      "post": {
        "tags": ["Workflows"],
        "summary": "Queue a workflow execution",
        "operationId": "runWorkflow",
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": { "parameters": { "type": "object", "additionalProperties": { "type": "string" } } }
              }
            }
          }
        },
        "responses": { "200": { "$ref": "#/components/responses/OkSummary" }, "404": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" } }
      }
    },
    "/api/mcp": {
      "post": {
        "tags": ["Meta"],
        "summary": "MCP JSON-RPC entrypoint",
        "description": "Send JSON-RPC 2.0 requests (single or batch) to drive the Specky MCP server. Methods: initialize, tools/list, tools/call, ping.",
        "operationId": "mcpJsonRpc",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object" } } } },
        "responses": { "200": { "description": "JSON-RPC response", "content": { "application/json": { "schema": { "type": "object" } } } }, "401": { "$ref": "#/components/responses/Unauthorized" } }
      },
      "get": {
        "tags": ["Meta"],
        "summary": "MCP discovery / SSE stream",
        "description": "Without `Accept: text/event-stream` → JSON server descriptor. With it → SSE keep-alive stream.",
        "operationId": "mcpDiscovery",
        "responses": { "200": { "description": "Descriptor or SSE stream" } }
      }
    },
    "/.well-known/agents.json": {
      "get": { "tags": ["Meta"], "summary": "Agent capability manifest", "operationId": "getAgentsManifest", "security": [], "responses": { "200": { "description": "Manifest", "content": { "application/json": { "schema": { "type": "object" } } } } } }
    },
    "/llms.txt": {
      "get": { "tags": ["Meta"], "summary": "LLM-friendly site index", "operationId": "getLlmsTxt", "security": [], "responses": { "200": { "description": "Plain-text index", "content": { "text/plain": { "schema": { "type": "string" } } } } } }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "sk_live_… or ext_…",
        "description": "Workspace API key from Specky → Settings → Developer. Sent as `Authorization: Bearer <token>`."
      }
    },
    "schemas": {
      "CaptureRequest": {
        "type": "object",
        "required": ["content"],
        "properties": {
          "content": { "type": "string", "maxLength": 10000 },
          "url": { "type": "string", "format": "uri" },
          "title": { "type": "string" },
          "source_type": { "type": "string", "enum": ["CHROME_EXTENSION", "COMPETITIVE_INTEL", "FEEDBACK", "MARKET_INTEL", "HIRING_INTEL"], "default": "CHROME_EXTENSION" }
        }
      },
      "CreateDocumentRequest": {
        "type": "object",
        "required": ["title", "type", "content"],
        "properties": {
          "title": { "type": "string" },
          "type": { "type": "string", "enum": ["prd", "spec", "research", "roadmap", "notes"] },
          "content": { "type": "string", "description": "Full markdown content." }
        }
      },
      "CreateInsightRequest": {
        "type": "object",
        "required": ["title", "content"],
        "properties": {
          "title": { "type": "string" },
          "content": { "type": "string" },
          "sentiment": { "type": "string", "enum": ["positive", "negative", "neutral"], "default": "neutral" },
          "tags": { "type": "array", "items": { "type": "string" } },
          "evidence_node_ids": { "type": "array", "items": { "type": "string" } }
        }
      },
      "CreateOpportunityRequest": {
        "type": "object",
        "required": ["title", "description", "impact", "effort"],
        "properties": {
          "title": { "type": "string" },
          "description": { "type": "string" },
          "impact": { "type": "string", "enum": ["high", "medium", "low"] },
          "effort": { "type": "string", "enum": ["high", "medium", "low"] },
          "parent_id": { "type": "string" }
        }
      },
      "CreateDecisionRequest": {
        "type": "object",
        "required": ["what", "rationale"],
        "properties": {
          "what": { "type": "string" },
          "rationale": { "type": "string" },
          "outcome": { "type": "string", "enum": ["expected", "confirmed", "revised", "reversed"], "default": "expected" },
          "decided_by": { "type": "string" },
          "evidence_node_ids": { "type": "array", "items": { "type": "string" } }
        }
      },
      "CreateExperimentRequest": {
        "type": "object",
        "required": ["hypothesis", "method"],
        "properties": {
          "hypothesis": { "type": "string" },
          "method": { "type": "string", "enum": ["interview", "survey", "prototype", "a_b_test", "fake_door", "concierge", "wizard_of_oz", "analytics", "other"] },
          "description": { "type": "string" },
          "success_criteria": { "type": "string" },
          "effort": { "type": "string", "enum": ["low", "medium", "high"], "default": "medium" },
          "time_to_run": { "type": "string" }
        }
      },
      "CreateOkrRequest": {
        "type": "object",
        "required": ["objective"],
        "properties": {
          "objective": { "type": "string" },
          "description": { "type": "string" },
          "quarter": { "type": "string" },
          "status": { "type": "string", "enum": ["on_track", "at_risk", "off_track"], "default": "on_track" },
          "owner": { "type": "string" },
          "key_results": {
            "type": "array",
            "maxItems": 5,
            "items": {
              "type": "object",
              "required": ["title", "target"],
              "properties": {
                "title": { "type": "string" },
                "target": { "type": "number" },
                "current": { "type": "number", "default": 0 }
              }
            }
          }
        }
      },
      "CreateTicketsRequest": {
        "type": "object",
        "required": ["feature_title", "tickets"],
        "properties": {
          "feature_title": { "type": "string" },
          "tickets": {
            "type": "array",
            "minItems": 1,
            "maxItems": 20,
            "items": {
              "type": "object",
              "required": ["type", "title", "description"],
              "properties": {
                "type": { "type": "string", "enum": ["epic", "story", "task"] },
                "title": { "type": "string" },
                "description": { "type": "string" },
                "priority": { "type": "string", "enum": ["critical", "high", "medium", "low"], "default": "medium" },
                "estimate": { "type": "number" },
                "labels": { "type": "array", "items": { "type": "string" } }
              }
            }
          }
        }
      },
      "StartResearchRequest": {
        "type": "object",
        "required": ["topic", "research_question"],
        "properties": {
          "topic": { "type": "string" },
          "research_question": { "type": "string" },
          "target_audience": { "type": "string" }
        }
      },
      "WebhookDelivery": {
        "type": "object",
        "description": "Body POSTed by Specky to subscribed webhook URLs. Each delivery carries `X-Specky-Event`, `X-Specky-Delivery-Id`, `X-Specky-Timestamp`, and `X-Specky-Signature` headers. Signature format: `t=<unix-sec>,v1=<hmac-sha256-hex>` over `<timestamp>.<rawBody>` with the per-webhook secret.",
        "properties": {
          "id": { "type": "string" },
          "event": { "type": "string", "enum": ["document.created", "insight.created", "ticket.created", "opportunity.created", "decision.created", "experiment.created", "okr.created"] },
          "workspace_id": { "type": "string", "format": "uuid" },
          "timestamp": { "type": "string", "format": "date-time" },
          "data": { "type": "object" }
        }
      },
      "Error": {
        "type": "object",
        "properties": { "error": { "type": "string" }, "details": { "type": "object" } }
      },
      "OkSummary": {
        "type": "object",
        "properties": {
          "ok": { "type": "boolean", "const": true },
          "summary": { "type": "string", "description": "Human-readable result text." }
        },
        "additionalProperties": true
      }
    },
    "responses": {
      "OkSummary": {
        "description": "Successful response. Always includes `summary` (string) and `ok: true`; mutating endpoints additionally include the created resource id under a tool-specific key.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OkSummary" } } }
      },
      "BadRequest": {
        "description": "Invalid request body or path",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "Unauthorized": {
        "description": "Missing or invalid bearer token",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "RateLimited": {
        "description": "Rate limit exceeded",
        "headers": {
          "X-RateLimit-Limit": { "schema": { "type": "integer" } },
          "X-RateLimit-Remaining": { "schema": { "type": "integer" } },
          "X-RateLimit-Reset": { "schema": { "type": "integer", "description": "Unix epoch seconds." } }
        },
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      }
    }
  }
}
