openapi: 3.1.0
info:
  title: EntryGo Core Protocol API
  version: 0.1.0
  summary: Lean machine-readable contract for the current EntryGo protocol surface.
  description: |
    EntryGo is the export orchestration layer for supply-chain applications.

    Use EntryGo when your application needs to:
    - sync products
    - sync orders
    - plan exports
    - execute exports
    - submit shipments to brokers or partners

    This document describes the current core EntryGo protocol endpoints implemented under `/api`.

    It is intentionally lean and tracks the audited runtime behavior:
    - export planning, execution, and inspection responses are modeled closely to the current schema-backed machine contract
    - `orders/sync` and `customs-packages/{id}` currently return persistence-shaped objects, so this spec documents the stable subset and allows additional implementation fields
servers:
  - url: http://localhost:3000
    description: Local development
security:
  - bearerAuth: []
tags:
  - name: Orders
  - name: Products
  - name: Exports
  - name: Setup
  - name: Customs Packages
  - name: System
paths:
  /api/products/sync:
    post:
      tags: [Products]
      operationId: syncProducts
      summary: Sync products into the account-scoped product store
      responses:
        '201':
          description: Products were synced successfully.
  /api/orders/sync:
    post:
      tags: [Orders]
      operationId: syncOrders
      summary: Sync orders into the account-scoped order store
      description: |
        Upserts orders by `externalId` within an account scope and replaces each order's line items.

        If both the `Idempotency-Key` header and body `idempotencyKey` are present, the header value wins.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKeyHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/OrderSyncRequest'
            example:
              accountId: acc_123
              items:
                - externalId: ord-ext-1
                  destinationCountry: US
                  destinationRegion: CA
                  destinationPostalCode: "94105"
                  items:
                    - productId: prod_123
                      quantity: 1
                      unitValueUsd: 110
                  metadata:
                    source: warehouse_ui
              idempotencyKey: orders-sync-001
      responses:
        '201':
          description: Orders were synced successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrderSyncResponse'
              example:
                data:
                  - id: ord_123
                    accountId: acc_123
                    externalId: ord-ext-1
                    destinationCountry: US
                    destinationRegion: CA
                    destinationPostalCode: "94105"
                    totalValueUsd: 110
                    createdAt: '2026-03-11T16:00:00.000Z'
                    items:
                      - id: oi_123
                        productId: prod_123
                        quantity: 1
                        unitValueUsd: 110
                        product:
                          id: prod_123
                          externalId: prod-ext-1
                          sku: JKT-001
                          name: Wool jacket
                          hsCode: '6201.93'
                          originCountry: CA
                          declaredValueUsd: 110
        '400':
          $ref: '#/components/responses/BadRequestError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /api/exports/plan:
    post:
      tags: [Exports]
      operationId: planExport
      summary: Plan an export batch
      description: |
        Planning primitive for the EntryGo protocol.

        If both the `Idempotency-Key` header and body `idempotencyKey` are present, the header value wins.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKeyHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PlanRequest'
            example:
              accountId: acc_123
              warehouseId: wh_123
              brokerId: br_123
              destinationCountry: US
              orderIds:
                - ord_123
                - ord_456
              dryRun: false
              idempotencyKey: plan-acc-123-001
      responses:
        '201':
          description: Export batch was planned successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PlanResponse'
              example:
                exportId: batch_123
                state: BATCHED
                batch_id: batch_123
                planHash: planhash_abc123
                status: BATCHED
                readinessState: BLOCKED
                complianceScore: 74
                validationMessages:
                  - code: MISSING_HS_CODE
                    message: Product JKT-001 is missing an HS code.
                    severity: error
                    field: orders[0].items[0].product.hs_code
                    product_id: prod_123
                    order_id: ord_123
                included_order_ids:
                  - ord_123
                  - ord_456
                excluded_order_ids: []
                flags:
                  - human_review_recommended
                missing_data:
                  - order_destination_postal_code_missing:ord_456
                recommended_strategy: Group U.S.-bound orders by operational readiness and generate an export batch before broker-facing review.
                estimated_import_savings_usd: 18.5
                next_recommended_action: review_flags_then_execute
                validation:
                  readiness_state: BLOCKED
                  compliance_score: 74
                  validation_messages:
                    - code: MISSING_HS_CODE
                      message: Product JKT-001 is missing an HS code.
                      severity: error
                      field: orders[0].items[0].product.hs_code
                      product_id: prod_123
                      order_id: ord_123
        '400':
          $ref: '#/components/responses/BadRequestError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '409':
          $ref: '#/components/responses/ConflictError'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /setup/status:
    get:
      tags: [Setup]
      operationId: getSetupStatus
      summary: Read machine-detectable setup status
      description: Returns the next required setup step for an authenticated or unauthenticated client.
      responses:
        '200':
          description: Setup status returned successfully.

  /exports/demo:
    post:
      tags: [Exports]
      operationId: createDemoExport
      summary: Create a demo export plan for the first successful loop
      responses:
        '201':
          description: Demo export plan created successfully.

  /api/exports/execute:
    post:
      tags: [Exports]
      operationId: executeExport
      summary: Execute a planned export batch
      description: |
        Execution primitive for the EntryGo protocol.

        If both the `Idempotency-Key` header and body `idempotencyKey` are present, the header value wins.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKeyHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ExecuteRequest'
            example:
              accountId: acc_123
              batchId: batch_123
              idempotencyKey: execute-acc-123-001
      responses:
        '201':
          description: Export batch execution completed or returned the current executed state.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExecuteResponse'
              example:
                exportId: batch_123
                state: COMPLETED
                batch_id: batch_123
                status: COMPLETED
                customs_package_id: cp_123
                planHash: planhash_abc123
                artifacts:
                  - artifact_type: cci_human_readable
                    filename: commercial_customs_invoice.html
                    generated_at: '2026-03-11T16:10:00.000Z'
                  - artifact_type: manifest
                    filename: manifest.json
                    generated_at: '2026-03-11T16:10:00.000Z'
                notification_ids:
                  - notif_123
                  - notif_456
                notifications:
                  - notif_123
                  - notif_456
                execution_timestamp: '2026-03-11T16:10:00.000Z'
                brokerSubmission:
                  id: bes_123
                  submission_type: broker_email
                  status: sent
                  recipient_email: broker@example.com
                  subject: Export package ready
                  sent_at: '2026-03-11T16:10:00.000Z'
                  error_message: null
        '400':
          $ref: '#/components/responses/BadRequestError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '404':
          $ref: '#/components/responses/NotFoundError'
        '409':
          $ref: '#/components/responses/ConflictError'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /api/exports/{id}:
    get:
      tags: [Exports]
      operationId: inspectExport
      summary: Inspect machine-readable export state
      parameters:
        - $ref: '#/components/parameters/ExportId'
      responses:
        '200':
          description: Current machine-readable export inspection state.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InspectResponse'
              example:
                exportId: batch_123
                status: COMPLETED
                state: COMPLETED
                readinessState: REVIEW_REQUIRED
                complianceScore: 74
                validationMessages:
                  - code: METALS_REVIEW_RECOMMENDED
                    message: Product JKT-001 should receive metals review before execution.
                    severity: warning
                    field: orders[0].items[0].product.section_232
                    product_id: prod_123
                    order_id: ord_123
                planHash: planhash_abc123
                artifacts:
                  - artifact_type: manifest
                    filename: manifest.json
                    generated_at: '2026-03-11T16:10:00.000Z'
                export:
                  batch_id: batch_123
                  status: COMPLETED
                  planHash: planhash_abc123
                  lifecycle_state: COMPLETED
                  plan:
                    exportId: batch_123
                    state: BATCHED
                    batch_id: batch_123
                    planHash: planhash_abc123
                    status: BATCHED
                    readinessState: REVIEW_REQUIRED
                    complianceScore: 74
                    validationMessages:
                      - code: METALS_REVIEW_RECOMMENDED
                        message: Product JKT-001 should receive metals review before execution.
                        severity: warning
                        field: orders[0].items[0].product.section_232
                        product_id: prod_123
                        order_id: ord_123
                    included_order_ids: [ord_123]
                    excluded_order_ids: []
                    flags: [human_review_recommended]
                    missing_data: []
                    recommended_strategy: Group U.S.-bound orders by operational readiness and generate an export batch before broker-facing review.
                    estimated_import_savings_usd: 18.5
                    next_recommended_action: review_flags_then_execute
                    validation:
                      readiness_state: REVIEW_REQUIRED
                      compliance_score: 74
                      validation_messages:
                        - code: METALS_REVIEW_RECOMMENDED
                          message: Product JKT-001 should receive metals review before execution.
                          severity: warning
                          field: orders[0].items[0].product.section_232
                          product_id: prod_123
                          order_id: ord_123
                  execution:
                    exportId: batch_123
                    state: COMPLETED
                    batch_id: batch_123
                    status: COMPLETED
                    customs_package_id: cp_123
                    planHash: planhash_abc123
                    artifacts:
                      - artifact_type: manifest
                        filename: manifest.json
                        generated_at: '2026-03-11T16:10:00.000Z'
                    notification_ids: [notif_123, notif_456]
                    notifications: [notif_123, notif_456]
                    execution_timestamp: '2026-03-11T16:10:00.000Z'
                    brokerSubmission:
                      id: bes_123
                      submission_type: broker_email
                      status: sent
                      recipient_email: broker@example.com
                      subject: Export package ready
                      sent_at: '2026-03-11T16:10:00.000Z'
                      error_message: null
                  inspection:
                    warehouse_id: wh_123
                    broker_id: br_123
                    destination_country: US
                    export_unit_count: 1
                    included_order_ids: [ord_123]
                    excluded_order_ids: []
                    flags: [human_review_recommended]
                    missing_data: []
                batch:
                  id: batch_123
                  state: COMPLETED
                  raw_state: COMPLETED
                  warehouse_id: wh_123
                  broker_id: br_123
                  destination_country: US
                  estimated_savings_usd: 18.5
                  next_recommended_action: monitor_export_batch_status
                  created_at: '2026-03-11T16:00:00.000Z'
                  executed_at: '2026-03-11T16:10:00.000Z'
                customsPackage:
                  id: cp_123
                  package_number: cp_ch123456
                  state: sent_to_broker
                  created_at: '2026-03-11T16:10:00.000Z'
                  artifacts:
                    - artifact_type: manifest
                      filename: manifest.json
                      generated_at: '2026-03-11T16:10:00.000Z'
                notifications:
                  - id: notif_123
                    recipient: broker@example.com
                    channel: email_placeholder
                    status: dispatched
                    created_at: '2026-03-11T16:10:00.000Z'
                brokerSubmission:
                  id: bes_123
                  submission_type: broker_email
                  status: sent
                  recipient_email: broker@example.com
                  subject: Export package ready
                  sent_at: '2026-03-11T16:10:00.000Z'
                  error_message: null
                snapshot:
                  account_id: acc_123
                  destination_country: US
                events:
                  - id: evt_123
                    export_id: batch_123
                    event_type: EXPORT_EXECUTED
                    payload:
                      customsPackageId: cp_123
                    created_at: '2026-03-11T16:10:00.000Z'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '404':
          $ref: '#/components/responses/NotFoundError'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /api/customs-packages/{id}:
    get:
      tags: [Customs Packages]
      operationId: getCustomsPackage
      summary: Retrieve a customs package
      description: |
        The current implementation returns a customs-package record plus additional related data.

        This schema documents the stable public subset and allows additional implementation fields so the
        contract does not freeze dashboard-oriented internals.
      parameters:
        - $ref: '#/components/parameters/CustomsPackageId'
      responses:
        '200':
          description: Customs package record.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CustomsPackage'
              example:
                id: cp_123
                exportBatchId: batch_123
                packageNumber: cp_ch123456
                state: sent_to_broker
                createdAt: '2026-03-11T16:10:00.000Z'
                artifacts:
                  - artifact_type: cci_json
                    filename: commercial_customs_invoice.json
                    generated_at: '2026-03-11T16:10:00.000Z'
                  - artifact_type: manifest
                    filename: manifest.json
                    generated_at: '2026-03-11T16:10:00.000Z'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '404':
          $ref: '#/components/responses/NotFoundError'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /api/system/health:
    get:
      tags: [System]
      operationId: getSystemHealth
      summary: Retrieve account-scoped system health
      responses:
        '200':
          description: System dependency health for the authenticated account context.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SystemHealth'
              example:
                status: ok
                timestamp: '2026-03-11T16:20:00.000Z'
                accountId: acc_123
                dependencies:
                  database:
                    status: ok
                    latency_ms: 12
                  api_key:
                    status: ok
                    account_id: acc_123
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /api/system/metrics:
    get:
      tags: [System]
      operationId: getSystemMetrics
      summary: Retrieve account-scoped protocol metrics
      responses:
        '200':
          description: Current protocol counters and rates for the authenticated account.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SystemMetrics'
              example:
                accountId: acc_123
                generatedAt: '2026-03-11T16:20:00.000Z'
                exportsPlanned: 12
                exportsExecuted: 8
                notificationSuccessRate: 0.875
                artifactGenerationCount: 8
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '500':
          $ref: '#/components/responses/InternalServerError'

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: API key

  parameters:
    IdempotencyKeyHeader:
      name: Idempotency-Key
      in: header
      required: false
      description: |
        Optional idempotency key. The current implementation reads the lowercase `idempotency-key`
        header; HTTP header names are case-insensitive.
      schema:
        type: string
    ExportId:
      name: id
      in: path
      required: true
      schema:
        type: string
        minLength: 3
      description: Export batch identifier.
    CustomsPackageId:
      name: id
      in: path
      required: true
      schema:
        type: string
        minLength: 3
      description: Customs package identifier.

  responses:
    BadRequestError:
      description: |
        Request failed before successful processing.

        The current implementation uses HTTP 400 for:
        - invalid JSON
        - schema validation failures
        - invalid order references during planning
        - warehouse or broker references outside account scope during planning
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          examples:
            invalidRequest:
              value:
                errorCode: INVALID_REQUEST
                message: Request body must be valid JSON.
            validationError:
              value:
                errorCode: VALIDATION_ERROR
                message: Request validation failed.
                field: orderIds
                details:
                  issues:
                    - path: orderIds
                      message: Array must contain at least 1 element(s)
                      code: too_small
            invalidOrderReference:
              value:
                errorCode: INVALID_ORDER_REFERENCE
                message: One or more orders could not be resolved.
                details:
                  requestedOrderIds:
                    - ord_123
                    - ord_999
                  foundOrderIds:
                    - ord_123
            invalidScopedReference:
              value:
                errorCode: INVALID_ACCOUNT_SCOPE
                message: Warehouse or broker is outside the current account scope.
    UnauthorizedError:
      description: API key was missing or invalid.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          example:
            errorCode: INVALID_API_KEY
            message: Missing API key.
    ForbiddenError:
      description: Payload account scope did not match the authenticated API key scope.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          example:
            errorCode: INVALID_ACCOUNT_SCOPE
            message: Payload accountId does not match API key scope.
            details:
              apiKeyAccountId: acc_123
              payloadAccountId: acc_999
    NotFoundError:
      description: Requested resource was not found in account scope.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          example:
            errorCode: NOT_FOUND
            message: Export batch not found.
    ConflictError:
      description: Request conflicts with current state or prior idempotent usage.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          examples:
            invalidState:
              value:
                errorCode: INVALID_EXPORT_STATE
                message: Export batch cannot be executed from current state.
                details:
                  currentState: PLANNED
            idempotencyConflict:
              value:
                errorCode: IDEMPOTENCY_CONFLICT
                message: Idempotency-Key was already used with a different execute payload.
                details:
                  batchId: batch_123
    InternalServerError:
      description: Unexpected server-side failure.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          example:
            errorCode: INTERNAL_ERROR
            message: An unexpected error occurred.

  schemas:
    ErrorEnvelope:
      type: object
      additionalProperties: false
      required: [errorCode, message]
      properties:
        errorCode:
          type: string
          description: Machine-readable error code in uppercase.
        message:
          type: string
        field:
          type: string
        details:
          type: object
          additionalProperties: true

    JsonObject:
      type:
        - object
        - 'null'
      additionalProperties: true

    ArtifactMetadata:
      type: object
      additionalProperties: false
      required: [artifact_type, filename, generated_at]
      properties:
        artifact_type:
          type: string
        filename:
          type: string
        generated_at:
          type: string
          format: date-time

    BrokerSubmission:
      type:
        - object
        - 'null'
      additionalProperties: false
      required:
        - id
        - submission_type
        - status
        - recipient_email
        - subject
        - sent_at
        - error_message
      properties:
        id:
          type: string
        submission_type:
          type: string
        status:
          type: string
        recipient_email:
          type: string
          format: email
        subject:
          type: string
        sent_at:
          type:
            - string
            - 'null'
          format: date-time
        error_message:
          type:
            - string
            - 'null'

    Notification:
      type: object
      additionalProperties: false
      required: [id, recipient, channel, status, created_at]
      properties:
        id:
          type: string
        recipient:
          type: string
        channel:
          type: string
        status:
          type: string
        created_at:
          type: string
          format: date-time

    ExportEvent:
      type: object
      additionalProperties: false
      required: [id, export_id, event_type, created_at]
      properties:
        id:
          type: string
        export_id:
          type: string
        event_type:
          type: string
        payload:
          type:
            - object
            - 'null'
          additionalProperties: true
        created_at:
          type: string
          format: date-time

    ValidationMessage:
      type: object
      additionalProperties: false
      required: [code, message, severity, field, product_id, order_id]
      properties:
        code:
          type: string
        message:
          type: string
        severity:
          type: string
          enum: [error, warning, info]
        field:
          type:
            - string
            - 'null'
        product_id:
          type:
            - string
            - 'null'
        order_id:
          type:
            - string
            - 'null'

    ValidationResult:
      type: object
      additionalProperties: false
      required: [readiness_state, compliance_score, validation_messages]
      properties:
        readiness_state:
          type: string
          enum: [READY, REVIEW_REQUIRED, BLOCKED]
        compliance_score:
          type: integer
          minimum: 0
          maximum: 100
        validation_messages:
          type: array
          items:
            $ref: '#/components/schemas/ValidationMessage'

    ProductSummary:
      type: object
      additionalProperties: true
      description: Stable subset of the product object currently nested under order items.
      required: [id, externalId, sku, name, originCountry, declaredValueUsd]
      properties:
        id:
          type: string
        externalId:
          type: string
        sku:
          type: string
        name:
          type: string
        hsCode:
          type:
            - string
            - 'null'
        originCountry:
          type: string
        declaredValueUsd:
          type: number

    OrderItemInput:
      type: object
      additionalProperties: false
      required: [productId, quantity, unitValueUsd]
      properties:
        productId:
          type: string
          minLength: 3
        quantity:
          type: integer
          minimum: 1
        unitValueUsd:
          type: number
          minimum: 0

    OrderSyncItem:
      type: object
      additionalProperties: false
      required: [externalId, destinationCountry, items]
      properties:
        externalId:
          type: string
          minLength: 1
        destinationCountry:
          type: string
          minLength: 2
          maxLength: 2
        destinationRegion:
          type: string
        destinationPostalCode:
          type: string
        items:
          type: array
          minItems: 1
          items:
            $ref: '#/components/schemas/OrderItemInput'
        metadata:
          $ref: '#/components/schemas/JsonObject'

    OrderSyncRequest:
      type: object
      additionalProperties: false
      required: [accountId, items]
      properties:
        accountId:
          type: string
          minLength: 3
        items:
          type: array
          minItems: 1
          items:
            $ref: '#/components/schemas/OrderSyncItem'
        idempotencyKey:
          type: string

    OrderItem:
      type: object
      additionalProperties: true
      description: Stable subset of the current order-item response shape.
      required: [productId, quantity, unitValueUsd]
      properties:
        id:
          type: string
        productId:
          type: string
        quantity:
          type: integer
        unitValueUsd:
          type: number
        product:
          $ref: '#/components/schemas/ProductSummary'

    Order:
      type: object
      additionalProperties: true
      description: |
        Stable subset of the current `orders/sync` response shape.
        The implementation may include additional persistence fields and nested product data.
      required: [id, accountId, externalId, destinationCountry, totalValueUsd, items]
      properties:
        id:
          type: string
        accountId:
          type: string
        externalId:
          type: string
        destinationCountry:
          type: string
        destinationRegion:
          type:
            - string
            - 'null'
        destinationPostalCode:
          type:
            - string
            - 'null'
        totalValueUsd:
          type: number
        metadata:
          $ref: '#/components/schemas/JsonObject'
        createdAt:
          type: string
          format: date-time
        items:
          type: array
          items:
            $ref: '#/components/schemas/OrderItem'

    OrderSyncResponse:
      type: object
      additionalProperties: false
      required: [data]
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/Order'

    PlanRequest:
      type: object
      additionalProperties: false
      required: [accountId, warehouseId, brokerId, orderIds]
      properties:
        accountId:
          type: string
          minLength: 3
        warehouseId:
          type: string
          minLength: 3
        brokerId:
          type: string
          minLength: 3
        destinationCountry:
          type: string
          minLength: 2
          maxLength: 2
          default: US
        orderIds:
          type: array
          minItems: 1
          items:
            type: string
            minLength: 3
        batchId:
          type: string
          minLength: 3
        dryRun:
          type: boolean
          default: false
        idempotencyKey:
          type: string

    PlanResponse:
      type: object
      additionalProperties: false
      required:
        - exportId
        - state
        - batch_id
        - planHash
        - status
        - readinessState
        - complianceScore
        - validationMessages
        - included_order_ids
        - excluded_order_ids
        - flags
        - missing_data
        - recommended_strategy
        - estimated_import_savings_usd
        - next_recommended_action
        - validation
      properties:
        exportId:
          type: string
        state:
          type: string
        batch_id:
          type: string
        planHash:
          type: string
        status:
          type: string
        readinessState:
          type: string
          enum: [READY, REVIEW_REQUIRED, BLOCKED]
        complianceScore:
          type: integer
          minimum: 0
          maximum: 100
        validationMessages:
          type: array
          items:
            $ref: '#/components/schemas/ValidationMessage'
        included_order_ids:
          type: array
          items:
            type: string
        excluded_order_ids:
          type: array
          items:
            type: string
        flags:
          type: array
          items:
            type: string
        missing_data:
          type: array
          items:
            type: string
        recommended_strategy:
          type: string
        estimated_import_savings_usd:
          type: number
          minimum: 0
        next_recommended_action:
          type: string
        validation:
          $ref: '#/components/schemas/ValidationResult'

    ExecuteRequest:
      type: object
      additionalProperties: false
      required: [accountId, batchId]
      properties:
        accountId:
          type: string
          minLength: 3
        batchId:
          type: string
          minLength: 3
        idempotencyKey:
          type: string

    ExecuteResponse:
      type: object
      additionalProperties: false
      required:
        - exportId
        - state
        - batch_id
        - status
        - customs_package_id
        - planHash
        - artifacts
        - notification_ids
        - notifications
        - execution_timestamp
        - brokerSubmission
      properties:
        exportId:
          type: string
        state:
          type: string
        batch_id:
          type: string
        status:
          type: string
        customs_package_id:
          type:
            - string
            - 'null'
        planHash:
          type:
            - string
            - 'null'
        artifacts:
          type: array
          items:
            $ref: '#/components/schemas/ArtifactMetadata'
        notification_ids:
          type: array
          items:
            type: string
        notifications:
          type: array
          items:
            type: string
        execution_timestamp:
          type:
            - string
            - 'null'
          format: date-time
        brokerSubmission:
          $ref: '#/components/schemas/BrokerSubmission'

    ExportInspectionSummary:
      type: object
      additionalProperties: false
      required:
        - warehouse_id
        - broker_id
        - destination_country
        - export_unit_count
        - included_order_ids
        - excluded_order_ids
        - flags
        - missing_data
      properties:
        warehouse_id:
          type: string
        broker_id:
          type: string
        destination_country:
          type: string
        export_unit_count:
          type: integer
          minimum: 0
        included_order_ids:
          type: array
          items:
            type: string
        excluded_order_ids:
          type: array
          items:
            type: string
        flags:
          type: array
          items:
            type: string
        missing_data:
          type: array
          items:
            type: string

    ExportMachineView:
      type: object
      additionalProperties: false
      required: [batch_id, status, planHash, lifecycle_state, plan, execution, inspection]
      properties:
        batch_id:
          type: string
        status:
          type: string
        planHash:
          type: string
        lifecycle_state:
          type: string
        plan:
          $ref: '#/components/schemas/PlanResponse'
        execution:
          $ref: '#/components/schemas/ExecuteResponse'
        inspection:
          $ref: '#/components/schemas/ExportInspectionSummary'

    ExportBatch:
      type: object
      additionalProperties: false
      required:
        - id
        - state
        - raw_state
        - warehouse_id
        - broker_id
        - destination_country
        - estimated_savings_usd
        - next_recommended_action
        - created_at
        - executed_at
      properties:
        id:
          type: string
        state:
          type: string
        raw_state:
          type: string
        warehouse_id:
          type: string
        broker_id:
          type: string
        destination_country:
          type: string
        estimated_savings_usd:
          type: number
          minimum: 0
        next_recommended_action:
          type: string
        created_at:
          type: string
          format: date-time
        executed_at:
          type:
            - string
            - 'null'
          format: date-time

    CustomsPackageSummary:
      type:
        - object
        - 'null'
      additionalProperties: false
      required: [id, package_number, state, created_at, artifacts]
      properties:
        id:
          type: string
        package_number:
          type: string
        state:
          type: string
        created_at:
          type: string
          format: date-time
        artifacts:
          type: array
          items:
            $ref: '#/components/schemas/ArtifactMetadata'

    InspectResponse:
      type: object
      additionalProperties: false
      required:
        - exportId
        - status
        - state
        - readinessState
        - complianceScore
        - validationMessages
        - planHash
        - artifacts
        - export
        - batch
        - customsPackage
        - notifications
        - brokerSubmission
        - snapshot
        - events
      properties:
        exportId:
          type: string
        status:
          type: string
        state:
          type: string
        readinessState:
          type: string
          enum: [READY, REVIEW_REQUIRED, BLOCKED]
        complianceScore:
          type: integer
          minimum: 0
          maximum: 100
        validationMessages:
          type: array
          items:
            $ref: '#/components/schemas/ValidationMessage'
        planHash:
          type: string
        artifacts:
          type: array
          items:
            $ref: '#/components/schemas/ArtifactMetadata'
        export:
          $ref: '#/components/schemas/ExportMachineView'
        batch:
          $ref: '#/components/schemas/ExportBatch'
        customsPackage:
          $ref: '#/components/schemas/CustomsPackageSummary'
        notifications:
          type: array
          items:
            $ref: '#/components/schemas/Notification'
        brokerSubmission:
          $ref: '#/components/schemas/BrokerSubmission'
        snapshot:
          $ref: '#/components/schemas/JsonObject'
        events:
          type: array
          items:
            $ref: '#/components/schemas/ExportEvent'

    CustomsPackage:
      type: object
      additionalProperties: true
      description: |
        Stable subset of the current customs package response shape.
        The implementation currently returns additional persistence and related-model fields.
      required: [id, state, packageNumber, artifacts]
      properties:
        id:
          type: string
        exportBatchId:
          type: string
        packageNumber:
          type: string
        state:
          type: string
        createdAt:
          type: string
          format: date-time
        artifacts:
          type: array
          items:
            $ref: '#/components/schemas/ArtifactMetadata'

    SystemHealthDatabaseDependency:
      type: object
      additionalProperties: false
      required: [status, latency_ms]
      properties:
        status:
          type: string
          enum: [ok, degraded]
        latency_ms:
          type:
            - integer
            - 'null'
          minimum: 0

    SystemHealthApiKeyDependency:
      type: object
      additionalProperties: false
      required: [status, account_id]
      properties:
        status:
          type: string
          enum: [ok]
        account_id:
          type: string

    SystemHealthDependencies:
      type: object
      additionalProperties: false
      required: [database, api_key]
      properties:
        database:
          $ref: '#/components/schemas/SystemHealthDatabaseDependency'
        api_key:
          $ref: '#/components/schemas/SystemHealthApiKeyDependency'

    SystemHealth:
      type: object
      additionalProperties: false
      required: [status, timestamp, accountId, dependencies]
      properties:
        status:
          type: string
          enum: [ok, degraded]
        timestamp:
          type: string
          format: date-time
        accountId:
          type: string
        dependencies:
          $ref: '#/components/schemas/SystemHealthDependencies'

    SystemMetrics:
      type: object
      additionalProperties: false
      required:
        - accountId
        - generatedAt
        - exportsPlanned
        - exportsExecuted
        - notificationSuccessRate
        - artifactGenerationCount
      properties:
        accountId:
          type: string
        generatedAt:
          type: string
          format: date-time
        exportsPlanned:
          type: integer
          minimum: 0
        exportsExecuted:
          type: integer
          minimum: 0
        notificationSuccessRate:
          type: number
          minimum: 0
          maximum: 1
        artifactGenerationCount:
          type: integer
          minimum: 0
