openapi: 3.1.0
info:
  title: EarlyCore API
  version: "2026-04-29"
  summary: AI security platform for European MSSPs. Programmatic access to Red Team scans, Runtime findings, agents, integrations, and reports.
  description: |
    The EarlyCore API lets MSSP partners and their clients automate AI security workflows: launch Red Team
    scans, query Runtime findings, manage monitored agents, configure webhooks, and pull auditor-ready
    compliance reports. REST over HTTPS with JSON bodies. EU-hosted, not subject to the US Cloud Act.

    Authentication uses API keys via the `Authorization: Bearer <key>` header. Each key has a role
    (`viewer`, `analyst`, `admin`) and a list of scopes (e.g. `scans:read`, `findings:write`,
    `agents:admin`). Agents and clients should request the narrowest scope set they need.

    Rate limit: 600 requests per minute per key. Exceeded requests return `429` with `Retry-After`.

    The MCP server at `mcp.earlycore.dev` exposes the same surface for agentic clients (Claude Code,
    Cursor) over the Model Context Protocol.
  contact:
    name: EarlyCore Developer Relations
    url: https://earlycore.dev/developers
    email: developers@earlycore.dev
  license:
    name: EarlyCore Terms of Service
    url: https://earlycore.dev/terms
  termsOfService: https://earlycore.dev/terms

servers:
  - url: https://api.earlycore.dev/v1
    description: Production (EU, multi-tenant)
  - url: https://api.sandbox.earlycore.dev/v1
    description: Sandbox (EU, free, mock data)

externalDocs:
  description: Developer portal, quickstarts, SDKs
  url: https://earlycore.dev/developers

tags:
  - name: Scans
    description: Red Team scans against AI agents and LLM endpoints. Maps to OWASP LLM Top 10, MITRE ATLAS, and EU AI Act controls.
  - name: Findings
    description: "Runtime detections from monitored AI agents: prompt injection, data exfiltration, drift, secrets leakage."
  - name: Agents
    description: AI agents under monitoring. One agent represents one production model deployment.
  - name: Reports
    description: Auditor-ready compliance evidence packs for DORA, NIS2, EU AI Act, GDPR, ISO 42001.
  - name: Webhooks
    description: Push event delivery for findings, scan completion, and integration sync.
  - name: Integrations
    description: Configure data sources (Bedrock, SageMaker, Azure OpenAI, Vertex AI, Logfire, LangChain) and destinations (Slack, Teams, Jira).

security:
  - BearerApiKey: []

paths:
  /scans:
    get:
      tags: [Scans]
      summary: List Red Team scans
      operationId: listScans
      security:
        - BearerApiKey: [scans:read]
      parameters:
        - { name: agent_id, in: query, schema: { type: string } }
        - { name: status, in: query, schema: { type: string, enum: [queued, running, completed, failed] } }
        - { name: limit, in: query, schema: { type: integer, default: 50, maximum: 200 } }
        - { name: cursor, in: query, schema: { type: string } }
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { $ref: "#/components/schemas/ScanList" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
    post:
      tags: [Scans]
      summary: Launch a Red Team scan
      operationId: createScan
      security:
        - BearerApiKey: [scans:write]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/ScanCreate" }
      responses:
        "201":
          description: Scan queued
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Scan" }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
  /scans/{scan_id}:
    get:
      tags: [Scans]
      summary: Retrieve a scan
      operationId: getScan
      security:
        - BearerApiKey: [scans:read]
      parameters:
        - { name: scan_id, in: path, required: true, schema: { type: string } }
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Scan" }
        "404": { $ref: "#/components/responses/NotFound" }
  /findings:
    get:
      tags: [Findings]
      summary: List Runtime findings
      operationId: listFindings
      security:
        - BearerApiKey: [findings:read]
      parameters:
        - { name: agent_id, in: query, schema: { type: string } }
        - { name: severity, in: query, schema: { type: string, enum: [P0, P1, P2, P3] } }
        - name: framework
          in: query
          schema:
            type: string
            enum: [dora, nis2, eu-ai-act, gdpr, iso-42001, owasp-llm-top-10, mitre-atlas]
        - { name: since, in: query, schema: { type: string, format: date-time } }
        - { name: limit, in: query, schema: { type: integer, default: 50, maximum: 200 } }
        - { name: cursor, in: query, schema: { type: string } }
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { $ref: "#/components/schemas/FindingList" }
  /findings/{finding_id}:
    get:
      tags: [Findings]
      summary: Retrieve a finding
      operationId: getFinding
      security:
        - BearerApiKey: [findings:read]
      parameters:
        - { name: finding_id, in: path, required: true, schema: { type: string } }
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Finding" }
        "404": { $ref: "#/components/responses/NotFound" }
    patch:
      tags: [Findings]
      summary: Update finding triage state
      operationId: updateFinding
      security:
        - BearerApiKey: [findings:write]
      parameters:
        - { name: finding_id, in: path, required: true, schema: { type: string } }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                status: { type: string, enum: [open, acknowledged, resolved, false-positive] }
                assignee_email: { type: string, format: email }
                note: { type: string, maxLength: 4000 }
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Finding" }
  /agents:
    get:
      tags: [Agents]
      summary: List monitored agents
      operationId: listAgents
      security:
        - BearerApiKey: [agents:read]
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { $ref: "#/components/schemas/AgentList" }
    post:
      tags: [Agents]
      summary: Register an agent for monitoring
      operationId: createAgent
      security:
        - BearerApiKey: [agents:write]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/AgentCreate" }
      responses:
        "201":
          description: Created
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Agent" }
  /reports:
    get:
      tags: [Reports]
      summary: List compliance reports
      operationId: listReports
      security:
        - BearerApiKey: [reports:read]
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { $ref: "#/components/schemas/ReportList" }
    post:
      tags: [Reports]
      summary: Generate a compliance report
      operationId: createReport
      security:
        - BearerApiKey: [reports:write]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [framework, agent_ids]
              properties:
                framework: { type: string, enum: [dora, nis2, eu-ai-act, gdpr, iso-42001, nist-ai-rmf] }
                agent_ids: { type: array, items: { type: string }, minItems: 1 }
                period_start: { type: string, format: date }
                period_end: { type: string, format: date }
      responses:
        "201":
          description: Report queued
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Report" }
  /webhooks:
    get:
      tags: [Webhooks]
      summary: List webhook subscriptions
      operationId: listWebhooks
      security:
        - BearerApiKey: [webhooks:read]
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { $ref: "#/components/schemas/WebhookList" }
    post:
      tags: [Webhooks]
      summary: Create a webhook subscription
      operationId: createWebhook
      security:
        - BearerApiKey: [webhooks:write]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/WebhookCreate" }
      responses:
        "201":
          description: Created
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Webhook" }
  /integrations:
    get:
      tags: [Integrations]
      summary: List configured integrations
      operationId: listIntegrations
      security:
        - BearerApiKey: [integrations:read]
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { $ref: "#/components/schemas/IntegrationList" }

components:
  securitySchemes:
    BearerApiKey:
      type: http
      scheme: bearer
      bearerFormat: API Key
      description: |
        API key issued in the EarlyCore developer portal. Send as `Authorization: Bearer ec_live_...`.
        Each key carries a role and a list of scopes; requests are denied if the scope is not granted.
        Sandbox keys are prefixed `ec_sandbox_`.
    OAuth2:
      type: oauth2
      description: OAuth 2.0 for partner-facing integrations and agentic clients (MCP). Authorization Code with PKCE.
      flows:
        authorizationCode:
          authorizationUrl: https://auth.earlycore.dev/oauth/authorize
          tokenUrl: https://auth.earlycore.dev/oauth/token
          refreshUrl: https://auth.earlycore.dev/oauth/token
          scopes:
            scans:read: Read Red Team scan results
            scans:write: Launch and cancel Red Team scans
            findings:read: Read Runtime findings
            findings:write: Triage Runtime findings
            agents:read: List monitored agents
            agents:write: Register and update monitored agents
            agents:admin: Delete monitored agents and rotate their keys
            reports:read: Read compliance reports
            reports:write: Generate compliance reports
            webhooks:read: List webhook subscriptions
            webhooks:write: Create and rotate webhook subscriptions
            integrations:read: List configured integrations
            integrations:write: Connect and disconnect integrations
  schemas:
    Scan:
      type: object
      properties:
        id: { type: string }
        agent_id: { type: string }
        status: { type: string, enum: [queued, running, completed, failed] }
        scan_packs: { type: array, items: { type: string } }
        started_at: { type: string, format: date-time }
        completed_at: { type: string, format: date-time, nullable: true }
        summary:
          type: object
          properties:
            total_attempts: { type: integer }
            successful_attacks: { type: integer }
            success_rate: { type: number, format: float }
    ScanCreate:
      type: object
      required: [agent_id, scan_packs]
      properties:
        agent_id: { type: string }
        scan_packs:
          type: array
          minItems: 1
          items:
            type: string
            enum:
              - owasp-llm-top-10
              - owasp-api-top-10
              - owasp-agentic-ai
              - mitre-atlas
              - eu-ai-act
              - dora
              - gdpr
              - iso-42001
              - nist-ai-rmf
              - nis2
              - rag
              - mcp
        max_attempts: { type: integer, minimum: 50, maximum: 5000, default: 500 }
    ScanList:
      type: object
      properties:
        data: { type: array, items: { $ref: "#/components/schemas/Scan" } }
        next_cursor: { type: string, nullable: true }
    Finding:
      type: object
      properties:
        id: { type: string }
        agent_id: { type: string }
        severity: { type: string, enum: [P0, P1, P2, P3] }
        category:
          type: string
          enum:
            - prompt-injection
            - data-exfiltration
            - sensitive-data-disclosure
            - secrets-leakage
            - model-drift
            - prompt-drift
            - latency-drift
            - error-rate-drift
        frameworks: { type: array, items: { type: string } }
        detected_at: { type: string, format: date-time }
        status: { type: string, enum: [open, acknowledged, resolved, false-positive] }
        evidence_url: { type: string, format: uri }
    FindingList:
      type: object
      properties:
        data: { type: array, items: { $ref: "#/components/schemas/Finding" } }
        next_cursor: { type: string, nullable: true }
    Agent:
      type: object
      properties:
        id: { type: string }
        display_name: { type: string }
        client_id: { type: string }
        source: { type: string, enum: [bedrock, sagemaker, azure-openai, vertex-ai, logfire, langchain, custom] }
        model: { type: string }
        created_at: { type: string, format: date-time }
    AgentCreate:
      type: object
      required: [display_name, source]
      properties:
        display_name: { type: string, maxLength: 120 }
        client_id: { type: string }
        source: { type: string, enum: [bedrock, sagemaker, azure-openai, vertex-ai, logfire, langchain, custom] }
        model: { type: string }
        tags: { type: object, additionalProperties: { type: string } }
    AgentList:
      type: object
      properties:
        data: { type: array, items: { $ref: "#/components/schemas/Agent" } }
        next_cursor: { type: string, nullable: true }
    Report:
      type: object
      properties:
        id: { type: string }
        framework: { type: string }
        status: { type: string, enum: [queued, rendering, ready, failed] }
        download_url: { type: string, format: uri, nullable: true }
        period_start: { type: string, format: date }
        period_end: { type: string, format: date }
    ReportList:
      type: object
      properties:
        data: { type: array, items: { $ref: "#/components/schemas/Report" } }
        next_cursor: { type: string, nullable: true }
    Webhook:
      type: object
      properties:
        id: { type: string }
        url: { type: string, format: uri }
        events:
          type: array
          items: { type: string, enum: [finding.created, finding.updated, scan.completed, report.ready] }
        secret_last4: { type: string }
    WebhookCreate:
      type: object
      required: [url, events]
      properties:
        url: { type: string, format: uri }
        events: { type: array, items: { type: string }, minItems: 1 }
    WebhookList:
      type: object
      properties:
        data: { type: array, items: { $ref: "#/components/schemas/Webhook" } }
    Integration:
      type: object
      properties:
        id: { type: string }
        type: { type: string, enum: [bedrock, sagemaker, azure-openai, vertex-ai, logfire, langchain, slack, teams, jira, email] }
        status: { type: string, enum: [connected, degraded, disconnected] }
        connected_at: { type: string, format: date-time }
    IntegrationList:
      type: object
      properties:
        data: { type: array, items: { $ref: "#/components/schemas/Integration" } }
    Error:
      type: object
      required: [error]
      properties:
        error:
          type: object
          properties:
            code: { type: string }
            message: { type: string }
            request_id: { type: string }
  responses:
    BadRequest:
      description: Invalid request
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
    Unauthorized:
      description: Missing or invalid API key
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
    Forbidden:
      description: Key lacks the required scope
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
