{
  "openapi": "3.1.0",
  "info": {
    "title": "Eodly API",
    "version": "1.0.0",
    "summary": "Read-only API for AI agents to fetch a team's end-of-day reports and roster from Eodly.",
    "description": "The Eodly API lets AI agents and scripts read a workspace's end-of-day reports (who shipped, who is silent, who is slipping) and its team roster. Authenticate with an org-scoped API key as a Bearer token. Create and manage keys in Eodly under Settings > API keys. See the full guide at https://eodly.io/developers.",
    "contact": { "name": "Eodly support", "email": "support@eodly.io", "url": "https://eodly.io/developers" },
    "license": { "name": "Proprietary", "url": "https://eodly.io/privacy" }
  },
  "servers": [{ "url": "https://eodly.io", "description": "Production" }],
  "externalDocs": { "description": "Developer & agent authentication guide", "url": "https://eodly.io/developers" },
  "security": [{ "ApiKeyAuth": [] }],
  "tags": [
    { "name": "Discovery", "description": "Public API discovery. No authentication required." },
    { "name": "Identity", "description": "Verify a key and discover its organization and scopes." },
    { "name": "Reports", "description": "End-of-day reports for the key's organization." },
    { "name": "Team", "description": "The team roster for the key's organization." }
  ],
  "x-api-key-scopes": {
    "reports:read": "Read end-of-day reports for the key's organization.",
    "team:read": "Read the team roster for the key's organization."
  },
  "paths": {
    "/api/v1": {
      "get": {
        "operationId": "getApiRoot",
        "tags": ["Discovery"],
        "summary": "API discovery root",
        "description": "Public, unauthenticated metadata about the Eodly API: version, how to authenticate, the available endpoints, and links to the spec and catalog. Exposes no organization data, so no API key is required.",
        "security": [],
        "responses": {
          "200": {
            "description": "API metadata.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiRoot" } } }
          }
        }
      }
    },
    "/api/v1/me": {
      "get": {
        "operationId": "getIdentity",
        "tags": ["Identity"],
        "summary": "Identify the calling API key",
        "description": "Returns the organization the key belongs to and the scopes it holds. Any valid key works; no specific scope is required.",
        "security": [{ "ApiKeyAuth": [] }],
        "responses": {
          "200": {
            "description": "The key's organization and scopes.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Identity" } } }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/api/v1/reports": {
      "get": {
        "operationId": "listReports",
        "tags": ["Reports"],
        "summary": "List recent end-of-day reports",
        "description": "Returns recent report summaries for the key's organization, most recent first. Requires the `reports:read` scope.",
        "security": [{ "ApiKeyAuth": [] }],
        "x-required-scopes": ["reports:read"],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Maximum number of reports to return (1-50, default 14).",
            "schema": { "type": "integer", "minimum": 1, "maximum": 50, "default": 14 }
          }
        ],
        "responses": {
          "200": {
            "description": "A list of report summaries.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "reports": { "type": "array", "items": { "$ref": "#/components/schemas/ReportSummary" } },
                    "count": { "type": "integer" }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" }
        }
      }
    },
    "/api/v1/reports/{id}": {
      "get": {
        "operationId": "getReport",
        "tags": ["Reports"],
        "summary": "Get one end-of-day report",
        "description": "Returns the full structured content of a single report in the key's organization. Requires the `reports:read` scope.",
        "security": [{ "ApiKeyAuth": [] }],
        "x-required-scopes": ["reports:read"],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The report id (UUID).",
            "schema": { "type": "string", "format": "uuid" }
          }
        ],
        "responses": {
          "200": {
            "description": "The full report.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Report" } } }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/api/v1/team": {
      "get": {
        "operationId": "listTeam",
        "tags": ["Team"],
        "summary": "List the team roster",
        "description": "Returns the team roster for the key's organization. Personal contact details (email, chat IDs) are intentionally omitted. Requires the `team:read` scope.",
        "security": [{ "ApiKeyAuth": [] }],
        "x-required-scopes": ["team:read"],
        "responses": {
          "200": {
            "description": "The team roster.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "members": { "type": "array", "items": { "$ref": "#/components/schemas/TeamMember" } },
                    "count": { "type": "integer" }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "Eodly API key (eodly_sk_...)",
        "description": "Send your Eodly API key as a Bearer token: `Authorization: Bearer eodly_sk_...`. Keys are organization-scoped and carry one or more scopes (`reports:read`, `team:read`). Create and revoke keys in Eodly under Settings > API keys. The `X-API-Key: eodly_sk_...` header is also accepted."
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing or invalid API key.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "Forbidden": {
        "description": "The key is valid but lacks the required scope.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "NotFound": {
        "description": "The resource does not exist in the key's organization.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      }
    },
    "schemas": {
      "ApiRoot": {
        "type": "object",
        "description": "Public API discovery metadata returned by GET /api/v1.",
        "properties": {
          "name": { "type": "string" },
          "version": { "type": "string" },
          "description": { "type": "string" },
          "documentation": { "type": "string", "format": "uri" },
          "openapi": { "type": "string", "format": "uri" },
          "api_catalog": { "type": "string", "format": "uri" },
          "authentication": {
            "type": "object",
            "properties": {
              "type": { "type": "string" },
              "scheme": { "type": "string" },
              "format": { "type": "string" },
              "header": { "type": "string" },
              "alternative_header": { "type": "string" },
              "how_to_obtain": { "type": "string", "format": "uri" },
              "scopes": { "type": "array", "items": { "type": "string" } }
            }
          },
          "endpoints": { "type": "object", "additionalProperties": { "type": "string" } },
          "rate_limit": { "type": "string" }
        }
      },
      "Identity": {
        "type": "object",
        "properties": {
          "organization": {
            "type": "object",
            "properties": {
              "id": { "type": "string", "format": "uuid" },
              "name": { "type": "string" }
            }
          },
          "scopes": { "type": "array", "items": { "type": "string", "enum": ["reports:read", "team:read"] } }
        }
      },
      "ReportSummary": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "scope": { "type": "string", "enum": ["org", "lead", "member"] },
          "scope_target_id": { "type": ["string", "null"], "format": "uuid" },
          "generated_at": { "type": "string", "format": "date-time" },
          "headline": { "type": ["string", "null"], "description": "One-line summary of the report." }
        }
      },
      "Report": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "scope": { "type": "string", "enum": ["org", "lead", "member"] },
          "scope_target_id": { "type": ["string", "null"], "format": "uuid" },
          "generated_at": { "type": "string", "format": "date-time" },
          "report": {
            "type": "object",
            "description": "The structured end-of-day report.",
            "additionalProperties": true,
            "properties": {
              "headline": { "type": "string" },
              "kpi_status": { "type": "array", "items": { "type": "object", "additionalProperties": true } },
              "shipped": { "type": "array", "items": { "type": "object", "additionalProperties": true } },
              "silent": { "type": "array", "items": { "type": "object", "additionalProperties": true } },
              "slipping": { "type": "array", "items": { "type": "object", "additionalProperties": true } },
              "blockers": { "type": "array", "items": { "type": "object", "additionalProperties": true } }
            }
          }
        }
      },
      "TeamMember": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "name": { "type": "string" },
          "role": { "type": "string", "enum": ["founder", "lead", "member"] },
          "department": { "type": ["string", "null"] },
          "is_external": { "type": "boolean" },
          "active": { "type": "boolean" }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": { "type": "string", "description": "A stable machine-readable error code." },
          "message": { "type": "string", "description": "A human-readable explanation." },
          "required_scope": { "type": "string", "description": "Present on 403: the scope the key was missing." }
        },
        "required": ["error"]
      }
    }
  }
}
