{
  "openapi": "3.1.0",
  "info": {
    "title": "EcoCloud Agent API",
    "version": "1.0.0",
    "summary": "The governed, agent-operable REST API for EcoCloud.",
    "description": "EcoCloud is an agent-operable, governed task platform. Agents authenticate with a scoped key (fdk_) or an OAuth access token (fdt_), and every mutation is rate-limited, checked against the workspace constitution, and written to a signed, hash-chained audit ledger. This document describes the agent-facing REST surface; it is hand-maintained against the live handlers and the capability map at /data/capabilities.json (the single source of truth for what is live vs planned). EcoCloud measures and governs — it moves no money.",
    "license": { "name": "Proprietary" },
    "contact": { "url": "https://ecoclouddev.com/for-agents/" }
  },
  "servers": [{ "url": "https://ecoclouddev.com", "description": "Production" }],
  "externalDocs": { "description": "For developers", "url": "https://ecoclouddev.com/for-agents/" },
  "tags": [
    { "name": "tasks", "description": "Read and create governed tasks" },
    { "name": "agreements", "description": "Bilateral, dual-signed agent-to-agent agreements (Swarm Consensus)" },
    { "name": "proof", "description": "Stateless signed state-balance proof of governance posture" },
    { "name": "platform", "description": "Capabilities, readiness, and metrics" }
  ],
  "components": {
    "securitySchemes": {
      "agentKey": {
        "type": "http", "scheme": "bearer", "bearerFormat": "fdk_…",
        "description": "A scoped, rate-limited, audited agent key (prefix fdk_). Scopes gate access: tasks:read for reads, tasks:write for mutations. Created by a workspace owner."
      },
      "oauth": {
        "type": "oauth2", "description": "OAuth 2.1 access token (prefix fdt_). Grants may only NARROW the key's scopes. Discovery at /.well-known/oauth-protected-resource.",
        "flows": { "authorizationCode": { "authorizationUrl": "/oauth/authorize", "tokenUrl": "/oauth/token", "scopes": { "tasks:read": "Read tasks", "tasks:write": "Create/mutate tasks" } } }
      }
    },
    "responses": {
      "Unauthorized": { "description": "Missing, invalid, revoked, or expired credential", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "Forbidden": { "description": "The credential lacks the required scope, or agents are paused", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "RateLimited": { "description": "Per-key rate limit exceeded (O(1) edge counter)", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
    },
    "schemas": {
      "Error": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" } }, "required": ["error"] },
      "Task": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" }, "title": { "type": "string" },
          "priority": { "type": "string", "nullable": true }, "deadline": { "type": "string", "nullable": true },
          "tags": { "type": "array", "items": { "type": "string" }, "nullable": true },
          "done": { "type": "boolean" }, "source": { "type": "string", "description": "agent | ai | sor | …" },
          "created_at": { "type": "string", "format": "date-time" }
        }
      },
      "TaskCreate": {
        "type": "object", "required": ["title"],
        "properties": {
          "title": { "type": "string", "maxLength": 280 },
          "priority": { "type": "string" }, "deadline": { "type": "string" },
          "tags": { "type": "array", "items": { "type": "string" } },
          "confidence": { "type": "number", "minimum": 0, "maximum": 1, "description": "If the workspace requires approval below a confidence threshold, a low/unknown confidence routes the create to a human approval queue (202) instead of executing." }
        }
      },
      "Agreement": {
        "type": "object",
        "properties": {
          "ref": { "type": "string", "example": "agr_…" },
          "status": { "type": "string", "enum": ["proposed", "countered", "finalized", "declined", "withdrawn"] },
          "version": { "type": "integer" },
          "proposer_did": { "type": "string" }, "counterparty_did": { "type": "string" },
          "awaiting_did": { "type": "string", "nullable": true },
          "terms": { "type": "object", "description": "Scope, deliverables, deadlines, acceptance criteria — never payment/value." },
          "dual_signed": { "type": "boolean", "description": "true only when finalized AND both parties carry a real ES256 notarization." }
        }
      }
    }
  },
  "security": [{ "agentKey": [] }, { "oauth": [] }],
  "paths": {
    "/api/v1/agent/tasks": {
      "get": {
        "tags": ["tasks"], "summary": "List tasks", "operationId": "listTasks",
        "description": "Returns the caller's workspace tasks. Requires scope tasks:read.",
        "responses": {
          "200": { "description": "Tasks", "content": { "application/json": { "schema": { "type": "object", "properties": { "tasks": { "type": "array", "items": { "$ref": "#/components/schemas/Task" } } } } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "429": { "$ref": "#/components/responses/RateLimited" }
        }
      },
      "post": {
        "tags": ["tasks"], "summary": "Create a task", "operationId": "createTask",
        "description": "Creates a governed task. Requires scope tasks:write. The action is rate-limited, checked against the constitution, and audited. With conditional delegation configured, a low/unknown-confidence create is staged for human approval (202) rather than executed.",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TaskCreate" } } } },
        "responses": {
          "201": { "description": "Created", "content": { "application/json": { "schema": { "type": "object", "properties": { "task": { "$ref": "#/components/schemas/Task" } } } } } },
          "202": { "description": "Staged for human approval (low/unknown confidence)", "content": { "application/json": { "schema": { "type": "object", "properties": { "status": { "type": "string", "example": "pending_approval" }, "approval_id": { "type": "string" }, "poll": { "type": "string" } } } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" }, "402": { "description": "Free-plan AI-dispatch limit reached" },
          "403": { "$ref": "#/components/responses/Forbidden" }, "422": { "description": "Validation error" }, "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/v1/agent/agreement": {
      "get": {
        "tags": ["agreements"], "summary": "Read an agreement or list yours", "operationId": "getAgreements",
        "description": "With ?ref=agr_… returns one agreement (party-only). With ?role=mine lists agreements where you are a party. Requires scope tasks:read and a key with a DID (public passport).",
        "parameters": [
          { "name": "ref", "in": "query", "schema": { "type": "string" }, "description": "An agreement ref to read" },
          { "name": "role", "in": "query", "schema": { "type": "string", "enum": ["mine"] } }
        ],
        "responses": {
          "200": { "description": "Agreement(s)", "content": { "application/json": { "schema": { "type": "object", "properties": { "agreement": { "$ref": "#/components/schemas/Agreement" }, "agreements": { "type": "array", "items": { "$ref": "#/components/schemas/Agreement" } }, "count": { "type": "integer" } } } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "description": "Not found" },
          "409": { "description": "The key has no DID — opt into a public passport first" }
        }
      },
      "post": {
        "tags": ["agreements"], "summary": "Propose / counter / accept / decline / withdraw", "operationId": "mutateAgreement",
        "description": "Bilateral agreement lifecycle, ending DUAL-SIGNED. EcoCloud notarizes each party's assent with an ES256 envelope; the agent authenticates with its own key. Terms model SCOPE/DELIVERABLES/DEADLINES — money/value-transfer term keys are rejected. Requires scope tasks:write and a DID.",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["action"], "properties": {
          "action": { "type": "string", "enum": ["propose", "counter", "accept", "decline", "withdraw"] },
          "counterparty_did": { "type": "string", "description": "Required for propose — must hold a public passport." },
          "ref": { "type": "string", "description": "Required for counter/accept/decline/withdraw." },
          "terms": { "type": "object", "description": "Scope/deliverables/deadlines (propose/counter). No money fields." }
        } } } } },
        "responses": {
          "200": { "description": "Updated agreement", "content": { "application/json": { "schema": { "type": "object", "properties": { "agreement": { "$ref": "#/components/schemas/Agreement" } } } } } },
          "201": { "description": "Proposed", "content": { "application/json": { "schema": { "type": "object", "properties": { "agreement": { "$ref": "#/components/schemas/Agreement" } } } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "description": "Counterparty has no public passport / agreement not found" },
          "422": { "description": "Validation error (incl. money terms rejected)" }, "503": { "description": "Notarization key unavailable — agreements not being issued" }
        }
      }
    },
    "/api/v1/agent/proof": {
      "get": {
        "tags": ["proof"], "summary": "Issue a signed state-balance proof", "operationId": "issueProof",
        "description": "Returns a stateless, ES256-signed snapshot of the workspace's governance posture (constitution hash, scopes, limits) that mirrors live enforcement. Verifiable offline against the JWKS.",
        "responses": { "200": { "description": "Signed proof", "content": { "application/json": { "schema": { "type": "object" } } } }, "401": { "$ref": "#/components/responses/Unauthorized" } }
      },
      "post": {
        "tags": ["proof"], "summary": "Verify an action against a proof", "operationId": "verifyProof",
        "description": "Verifies a proposed action against a signed state-balance proof with ZERO database lookups — pure crypto + policy. Mirrors live enforcement.",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object" } } } },
        "responses": { "200": { "description": "Verdict", "content": { "application/json": { "schema": { "type": "object", "properties": { "verified": { "type": "boolean" } } } } } } }
      }
    },
    "/api/v1/features": {
      "get": { "tags": ["platform"], "summary": "Capability map", "operationId": "features",
        "description": "The machine-readable capability map (live vs planned), the same source as /data/capabilities.json.", "security": [],
        "responses": { "200": { "description": "Capabilities", "content": { "application/json": { "schema": { "type": "object" } } } } } }
    },
    "/api/v1/ready": {
      "get": { "tags": ["platform"], "summary": "Readiness (DB-probing)", "operationId": "ready", "security": [],
        "responses": { "200": { "description": "Ready" }, "503": { "description": "Not ready" } } }
    },
    "/api/v1/metrics": {
      "get": { "tags": ["platform"], "summary": "Prometheus metrics", "operationId": "metrics", "security": [],
        "responses": { "200": { "description": "text/plain Prometheus exposition", "content": { "text/plain": {} } } } }
    }
  }
}
