openapi: 3.1.0
info:
  title: Forest OS - FGIS LK Negotiated Exchange API
  version: 0.1.0
  description: >-
    API external analytical observations. It is ready for sandbox negotiation,
    but is not an official FGIS LK contract until approved by the operator.
servers:
  - url: https://fgis-exchange.forestosglobal.com
    description: Planned mTLS endpoint; disabled until operator approval
security:
  - mutualTLS: []
  - hmacKeyId: []
    hmacTimestamp: []
    hmacNonce: []
    hmacSignature: []
paths:
  /health:
    get:
      security: []
      operationId: health
      responses:
        '200': {description: Process is alive}
  /ready:
    get:
      security: []
      operationId: readiness
      responses:
        '200': {description: Local dependencies are ready}
        '503': {description: A local dependency is unavailable}
  /v1/capabilities:
    get:
      operationId: capabilities
      responses:
        '200':
          description: Supported datasets, formats and protocol surfaces
          content:
            application/json:
              schema: {type: object}
  /v1/schemas:
    get:
      operationId: schemas
      responses:
        '200':
          description: Versioned schema registry and installation status
          content:
            application/json:
              schema: {type: object}
  /v1/batches:
    post:
      operationId: createBatch
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: '#/components/schemas/ExportRequest'}
      responses:
        '201':
          description: A new immutable export batch was created
          content:
            application/json:
              schema: {$ref: '#/components/schemas/BatchResponse'}
        '200': {description: Idempotent replay returned the existing batch}
        '409': {description: The idempotency key belongs to a different request}
        '422': {description: Request validation failed}
  /v1/batches/{batchId}:
    get:
      operationId: getBatch
      parameters:
        - $ref: '#/components/parameters/BatchId'
      responses:
        '200':
          description: Batch state and checksums
          content:
            application/json:
              schema: {$ref: '#/components/schemas/BatchResponse'}
        '404': {description: Batch not found}
  /v1/batches/{batchId}/payload:
    get:
      operationId: getBatchPayload
      parameters:
        - $ref: '#/components/parameters/BatchId'
      responses:
        '200':
          description: Immutable payload; X-Payload-SHA256 and ETag are returned
          content:
            application/xml: {schema: {type: string}}
            application/json: {schema: {type: object}}
            application/geo+json: {schema: {type: object}}
  /v1/batches/{batchId}/ack:
    post:
      operationId: acknowledgeBatch
      parameters:
        - $ref: '#/components/parameters/BatchId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [ack_id]
              properties:
                ack_id: {type: string, minLength: 1, maxLength: 255}
      responses:
        '200': {description: Delivery acknowledgement recorded}
        '422': {description: Batch has not reached the delivered state}
  /v1/validate:
    post:
      operationId: validateXml
      parameters:
        - name: profile
          in: query
          required: true
          schema: {type: string}
      requestBody:
        required: true
        content:
          application/xml: {schema: {type: string}}
      responses:
        '200': {description: XML is valid for the selected profile}
        '422': {description: XML or profile validation failed}
  /soap/v1:
    post:
      operationId: soapExchange
      description: >-
        SOAP 1.2 compatibility endpoint for the negotiated Forest OS contract.
        It is not an official FGIS LK WSDL.
      requestBody:
        required: true
        content:
          application/soap+xml: {schema: {type: string}}
      responses:
        '200': {description: SOAP response}
components:
  securitySchemes:
    mutualTLS: {type: mutualTLS}
    hmacKeyId: {type: apiKey, in: header, name: X-Forest-Key-Id}
    hmacTimestamp: {type: apiKey, in: header, name: X-Forest-Timestamp}
    hmacNonce: {type: apiKey, in: header, name: X-Forest-Nonce}
    hmacSignature: {type: apiKey, in: header, name: X-Forest-Signature}
  parameters:
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: true
      schema: {type: string, minLength: 16, maxLength: 160}
    BatchId:
      name: batchId
      in: path
      required: true
      schema: {type: string, pattern: '^fos-batch-[0-9a-f]{32}$'}
  schemas:
    ExportRequest:
      type: object
      additionalProperties: false
      properties:
        datasets:
          type: array
          minItems: 1
          uniqueItems: true
          items: {type: string, enum: [ndvi, lst, soil_moisture, active_fire, burn_history]}
        format: {type: string, enum: [xml, json, geojson], default: xml}
        bbox:
          type: array
          minItems: 4
          maxItems: 4
          items: {type: number}
          description: min_lon,min_lat,max_lon,max_lat in EPSG:4326
        observed_from: {type: string, pattern: '^\\d{4}-\\d{2}(-\\d{2})?$'}
        observed_to: {type: string, pattern: '^\\d{4}-\\d{2}(-\\d{2})?$'}
        limit: {type: integer, minimum: 1, maximum: 100000}
    BatchResponse:
      type: object
      properties:
        ok: {type: boolean}
        created: {type: boolean}
        batch:
          type: object
          properties:
            batch_id: {type: string}
            state: {type: string, enum: [READY, SENDING, DELIVERED, FAILED, ACKNOWLEDGED]}
            format: {type: string}
            record_count: {type: integer}
            source_sha256: {type: string, pattern: '^[0-9a-f]{64}$'}
            payload_sha256: {type: string, pattern: '^[0-9a-f]{64}$'}

