openapi: 3.1.0
info:
  title: Forgium Agent Public API
  version: 2.0.0
  description: |
    Stable public contract for sending messages to Forgium Agent and managing
    host-application capabilities. Authenticate with the API key issued for
    the target environment.
  contact:
    name: Forgium platform administrator
  license:
    name: Forgium public documentation terms
    identifier: LicenseRef-Forgium-Public-Docs
servers:
  - url: https://{apiHost}
    description: Use the host from FORGIUM_AGENT_BASE_URL. Do not infer a host from this document.
    variables:
      apiHost:
        default: api.forgium.dev
        description: Hostname only, copied from FORGIUM_AGENT_BASE_URL.
security:
  - bearerAuth: []
tags:
  - name: System
    description: Availability checks.
  - name: HTTP channel
    description: Synchronous text messaging.
  - name: Capabilities
    description: Self-service host-application capability lifecycle.
paths:
  /health:
    get:
      operationId: getHealth
      summary: Check public API availability
      security: []
      tags: [System]
      responses:
        '200':
          description: Public API is available.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HealthResponse'
              example:
                status: ok
                service: agent-api
        '400':
          description: Invalid health-check request.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '503':
          description: Public API is temporarily unavailable.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /v1/channels/http/messages:
    post:
      operationId: sendHttpMessage
      summary: Send a text message and receive a synchronous response
      tags: [HTTP channel]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/HttpChannelRequest'
            example:
              user_id: mobile_user_123
              message:
                type: text
                body: Hello, can you help me?
              metadata:
                platform: ios
                app_version: 1.0.0
      responses:
        '200':
          description: Agent response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HttpChannelResponse'
              example:
                conversation_id: conv_abc123def456
                message:
                  id: msg_abc123def456
                  role: assistant
                  type: text
                  body: Hello, how can I help?
        '401':
          $ref: '#/components/responses/Unauthorized'
        '405':
          $ref: '#/components/responses/HttpMethodNotAllowed'
        '409':
          $ref: '#/components/responses/HttpDuplicateEvent'
        '422':
          $ref: '#/components/responses/HttpValidationError'
        '500':
          $ref: '#/components/responses/HttpRuntimeError'

  /v1/capability-credentials/{credentialRef}:
    put:
      operationId: configureCapabilityCredential
      summary: Store or rotate a host-endpoint credential
      description: The secret is encrypted at rest and is never returned or included in a capability manifest.
      tags: [Capabilities]
      parameters:
        - $ref: '#/components/parameters/CredentialRef'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CredentialRequest'
            example:
              auth_type: bearer
              secret: endpoint-token-from-secret-manager
      responses:
        '201':
          description: Credential configured. The secret is not returned.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CredentialResponse'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '413':
          description: Request exceeds the credential size limit.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '503':
          description: Credential vault is unavailable.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /v1/capabilities:
    get:
      operationId: listCapabilities
      summary: List capabilities available to the API key
      tags: [Capabilities]
      responses:
        '200':
          description: Capability definitions scoped to the API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CapabilityListResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /v1/capabilities/import:
    post:
      operationId: importCapabilities
      summary: Import capabilities as drafts
      description: Import does not activate a capability. A contract test is required before activation.
      tags: [Capabilities]
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CapabilityManifest'
      responses:
        '201':
          description: Draft capabilities created.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CapabilityImportResponse'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '409':
          $ref: '#/components/responses/Conflict'

  /v1/capabilities/{capabilityId}:
    get:
      operationId: getCapability
      summary: Get one capability
      tags: [Capabilities]
      parameters:
        - $ref: '#/components/parameters/CapabilityId'
      responses:
        '200':
          description: Capability definition and revision history.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CapabilityDetailResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    patch:
      operationId: reviseCapability
      summary: Create a draft revision of a capability
      description: If-Match must contain the current numeric revision. The revised capability must be tested again.
      tags: [Capabilities]
      parameters:
        - $ref: '#/components/parameters/CapabilityId'
        - $ref: '#/components/parameters/IfMatch'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CapabilityPatchRequest'
      responses:
        '200':
          description: Draft revision created.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CapabilityLifecycleResponse'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'

  /v1/capabilities/{capabilityId}/test:
    post:
      operationId: testCapability
      summary: Run a contract test for a read capability
      description: A passed test is tied to the current revision and is required for activation.
      tags: [Capabilities]
      parameters:
        - $ref: '#/components/parameters/CapabilityId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CapabilityTestRequest'
            example:
              arguments:
                name: Juan Pérez
      responses:
        '200':
          description: Contract test result.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CapabilityTestResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'

  /v1/capabilities/{capabilityId}/activate:
    post:
      operationId: activateCapability
      summary: Activate a tested capability
      tags: [Capabilities]
      parameters:
        - $ref: '#/components/parameters/CapabilityId'
      responses:
        '200':
          description: Capability is active.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CapabilityLifecycleResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'

  /v1/capabilities/{capabilityId}/disable:
    post:
      operationId: disableCapability
      summary: Disable a capability
      tags: [Capabilities]
      parameters:
        - $ref: '#/components/parameters/CapabilityId'
      responses:
        '200':
          description: Capability is disabled.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CapabilityLifecycleResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: API key issued out of band by the Forgium administrator.

  parameters:
    CredentialRef:
      name: credentialRef
      in: path
      required: true
      schema:
        type: string
        pattern: '^cred_[A-Za-z0-9_-]{3,120}$'
        minLength: 7
        maxLength: 128
    CapabilityId:
      name: capabilityId
      in: path
      required: true
      schema:
        type: string
        pattern: '^cap_[a-z0-9]+$'
        minLength: 8
        maxLength: 80
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: true
      schema:
        type: string
        minLength: 1
        maxLength: 200
    IfMatch:
      name: If-Match
      in: header
      required: true
      schema:
        type: string
        minLength: 1
        maxLength: 80

  schemas:
    HealthResponse:
      type: object
      additionalProperties: false
      required: [status, service]
      properties:
        status:
          type: string
          enum: [ok]
          maxLength: 16
        service:
          type: string
          maxLength: 64

    HttpChannelRequest:
      type: object
      additionalProperties: false
      required: [message]
      properties:
        conversation_id:
          type: string
          minLength: 1
          maxLength: 128
        user_id:
          type: string
          minLength: 1
          maxLength: 200
        message:
          $ref: '#/components/schemas/TextMessageRequest'
        metadata:
          type: object
          additionalProperties:
            type: string
            maxLength: 500
          maxProperties: 30
    TextMessageRequest:
      type: object
      additionalProperties: false
      required: [type, body]
      properties:
        type:
          type: string
          enum: [text]
          maxLength: 16
        body:
          type: string
          minLength: 1
          maxLength: 10000
    HttpChannelResponse:
      type: object
      additionalProperties: false
      required: [conversation_id, message]
      properties:
        conversation_id:
          type: string
          minLength: 1
          maxLength: 128
        message:
          $ref: '#/components/schemas/AssistantMessage'
    AssistantMessage:
      type: object
      additionalProperties: false
      required: [id, role, type, body]
      properties:
        id:
          type: string
          minLength: 1
          maxLength: 128
        role:
          type: string
          enum: [assistant]
          maxLength: 16
        type:
          type: string
          enum: [text]
          maxLength: 16
        body:
          type: string
          maxLength: 20000

    CredentialRequest:
      type: object
      additionalProperties: false
      required: [auth_type, secret]
      properties:
        auth_type:
          type: string
          enum: [bearer, api_key, basic]
          maxLength: 16
        header_name:
          type: string
          enum: [x-api-key, x-api-token]
          maxLength: 32
        secret:
          type: string
          minLength: 1
          maxLength: 4096
    CredentialResponse:
      type: object
      additionalProperties: false
      required: [credential_ref, status]
      properties:
        credential_ref:
          type: string
          maxLength: 128
        status:
          type: string
          enum: [configured]
          maxLength: 32

    CapabilityManifest:
      type: object
      additionalProperties: false
      required: [domain, capabilities]
      properties:
        domain:
          type: string
          pattern: '^[a-z][a-z0-9_-]{1,63}$'
          minLength: 2
          maxLength: 64
        capabilities:
          type: array
          minItems: 1
          maxItems: 50
          items:
            $ref: '#/components/schemas/CapabilityDefinition'
    CapabilityDefinition:
      type: object
      additionalProperties: false
      required: [name, description, when_to_use, method, url, auth, safe_headers, input_schema, output_schema, side_effect, requires_approval, timeout_ms, retry_count]
      properties:
        name:
          type: string
          pattern: '^[a-z][a-z0-9_]{1,63}$'
          minLength: 2
          maxLength: 64
        description:
          type: string
          minLength: 1
          maxLength: 1000
        when_to_use:
          type: string
          minLength: 1
          maxLength: 1000
        method:
          type: string
          enum: [GET, POST, PUT, PATCH, DELETE]
          maxLength: 6
        url:
          type: string
          format: uri
          pattern: '^https://'
          maxLength: 2048
        allowed_host:
          type: string
          maxLength: 253
        auth:
          $ref: '#/components/schemas/CapabilityAuth'
        safe_headers:
          type: object
          additionalProperties: false
          properties:
            accept:
              type: string
              maxLength: 100
            content-type:
              type: string
              enum: [application/json]
              maxLength: 100
        input_schema:
          $ref: '#/components/schemas/JsonSchemaDocument'
        output_schema:
          $ref: '#/components/schemas/JsonSchemaDocument'
        business_rules:
          type: array
          maxItems: 50
          items:
            type: string
            maxLength: 1000
        examples:
          type: array
          maxItems: 20
          items:
            type: object
            additionalProperties: true
        side_effect:
          type: string
          enum: [read, create, update, delete, external_send]
          maxLength: 32
        requires_approval:
          type: boolean
        idempotency_header:
          type: string
          maxLength: 100
        timeout_ms:
          type: integer
          minimum: 1000
          maximum: 10000
        retry_count:
          type: integer
          minimum: 0
          maximum: 1
        cache_ttl_seconds:
          type: integer
          minimum: 0
          maximum: 86400
    CapabilityAuth:
      type: object
      additionalProperties: false
      required: [type, credential_ref]
      properties:
        type:
          type: string
          enum: [bearer, api_key, basic]
          maxLength: 16
        credential_ref:
          type: string
          pattern: '^cred_[A-Za-z0-9_-]{3,120}$'
          maxLength: 128
        header_name:
          type: string
          enum: [x-api-key, x-api-token]
          maxLength: 32
    JsonSchemaDocument:
      type: object
      description: Bounded JSON Schema document used to validate capability arguments or results.
      additionalProperties: true
    CapabilityImportResponse:
      type: object
      additionalProperties: false
      required: [capabilities]
      properties:
        capabilities:
          type: array
          maxItems: 50
          items:
            $ref: '#/components/schemas/CapabilityLifecycleResponse'
    CapabilityListResponse:
      type: object
      additionalProperties: false
      required: [capabilities]
      properties:
        capabilities:
          type: array
          maxItems: 1000
          items:
            $ref: '#/components/schemas/CapabilitySummary'
    CapabilitySummary:
      type: object
      unevaluatedProperties: false
      required: [id, name, description, when_to_use, input_schema, output_schema, business_rules, examples, side_effect, requires_approval, revision, lifecycle_status]
      properties:
        id:
          type: string
          maxLength: 80
        name:
          type: string
          maxLength: 64
        description:
          type: string
          maxLength: 1000
        when_to_use:
          type: string
          maxLength: 1000
        input_schema:
          $ref: '#/components/schemas/JsonSchemaDocument'
        output_schema:
          $ref: '#/components/schemas/JsonSchemaDocument'
        business_rules:
          type: array
          maxItems: 50
          items:
            type: string
            maxLength: 1000
        examples:
          type: array
          maxItems: 20
          items:
            type: object
            additionalProperties: true
        side_effect:
          type: string
          maxLength: 32
        requires_approval:
          type: boolean
        revision:
          type: integer
          minimum: 1
        lifecycle_status:
          type: string
          enum: [draft, active, disabled]
          maxLength: 16
        credential_configured:
          type: boolean
        updated_at:
          type: string
          format: date-time
          maxLength: 64
    CapabilityDetailResponse:
      allOf:
        - $ref: '#/components/schemas/CapabilitySummary'
        - type: object
          unevaluatedProperties: false
          required: [method, url, revisions]
          properties:
            method:
              type: string
              maxLength: 6
            url:
              type: string
              format: uri
              maxLength: 2048
            revisions:
              type: array
              maxItems: 100
              items:
                $ref: '#/components/schemas/CapabilityRevision'
    CapabilityRevision:
      type: object
      additionalProperties: false
      required: [revision, change_type, actor_type, actor_id, created_at]
      properties:
        revision:
          type: integer
          minimum: 1
        change_type:
          type: string
          maxLength: 32
        actor_type:
          type: string
          maxLength: 32
        actor_id:
          type: string
          maxLength: 128
        created_at:
          type: string
          format: date-time
          maxLength: 64
    CapabilityPatchRequest:
      type: object
      additionalProperties: false
      required: [capability]
      properties:
        domain:
          type: string
          maxLength: 64
        capability:
          $ref: '#/components/schemas/CapabilityDefinition'
    CapabilityTestRequest:
      type: object
      additionalProperties: false
      required: [arguments]
      properties:
        arguments:
          type: object
          additionalProperties: true
    CapabilityTestResponse:
      type: object
      additionalProperties: false
      required: [status, capability_id, revision]
      properties:
        status:
          type: string
          enum: [passed, failed]
          maxLength: 16
        capability_id:
          type: string
          maxLength: 80
        revision:
          type: integer
          minimum: 1
        error_code:
          type: string
          maxLength: 100
    CapabilityLifecycleResponse:
      type: object
      additionalProperties: false
      required: [id, lifecycle_status, revision]
      properties:
        id:
          type: string
          maxLength: 80
        lifecycle_status:
          type: string
          enum: [draft, active, disabled]
          maxLength: 16
        revision:
          type: integer
          minimum: 1

    ErrorResponse:
      type: object
      additionalProperties: false
      required: [error, code]
      properties:
        error:
          type: string
          maxLength: 500
        code:
          type: string
          maxLength: 100
    HttpErrorResponse:
      type: object
      additionalProperties: false
      required: [error]
      properties:
        error:
          type: object
          additionalProperties: false
          required: [code, message]
          properties:
            code:
              type: string
              maxLength: 100
            message:
              type: string
              maxLength: 500
            details:
              type: object
              additionalProperties: true

  responses:
    Unauthorized:
      description: API key is missing, invalid, expired, or revoked.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error: Invalid API key
            code: UNAUTHORIZED
    ValidationError:
      description: Request body or parameter is invalid.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error: VALIDATION_ERROR
            code: VALIDATION_ERROR
    HttpMethodNotAllowed:
      description: HTTP method is not supported for this route.
      headers:
        Allow:
          schema:
            type: string
            maxLength: 100
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/HttpErrorResponse'
    HttpDuplicateEvent:
      description: The message event was already processed.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/HttpErrorResponse'
    HttpValidationError:
      description: The HTTP channel request is invalid.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/HttpErrorResponse'
    HttpRuntimeError:
      description: The agent runtime could not produce a response.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/HttpErrorResponse'
    NotFound:
      description: Capability does not exist for this API key.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    Conflict:
      description: The requested operation conflicts with the current capability state.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
