{
  "spec": {
    "openapi": "3.0.3",
    "title": "NovaPay Payout API",
    "version": "1.0.0",
    "source": "/data/web/20260906-191850-8d9be3/input/spec.yaml"
  },
  "endpoints": [
    {
      "role": "create",
      "method": "POST",
      "path": "/payouts",
      "operation_id": "createPayout",
      "confidence": 0.95,
      "outside_contract": false
    },
    {
      "role": "status",
      "method": "GET",
      "path": "/payouts/{payout_id}",
      "operation_id": "getPayoutStatus",
      "confidence": 0.9,
      "outside_contract": false
    },
    {
      "role": "cancel",
      "method": "POST",
      "path": "/payouts/{payout_id}/cancel",
      "operation_id": "cancelPayout",
      "confidence": 0.95,
      "outside_contract": true
    },
    {
      "role": "webhook",
      "method": "POST",
      "path": "/webhooks/payout",
      "operation_id": "payoutWebhook",
      "confidence": 0.9,
      "outside_contract": false
    },
    {
      "role": "balance",
      "method": "GET",
      "path": "/balance",
      "operation_id": "getBalance",
      "confidence": 0.85,
      "outside_contract": true
    }
  ],
  "auth": {
    "type": "api_key",
    "scheme_name": "ApiKeyAuth",
    "header": "X-API-Key",
    "prefix": null,
    "credential_key": "api_key",
    "credential_keys": [
      "api_key"
    ],
    "location": "header",
    "param_name": "X-API-Key"
  },
  "statuses": {
    "field_path": [
      "status"
    ],
    "map": {
      "pending": "in_progress",
      "processing": "in_progress",
      "completed": "approved",
      "failed": "rejected",
      "cancelled": "rejected"
    },
    "unmapped": [],
    "response_id_path": [
      "id"
    ],
    "response_status_path": [
      "status"
    ],
    "response_id_confidence": 0.95
  },
  "errors": {
    "rows": [
      {
        "status": 400,
        "role": "create",
        "pointer": "#/paths/~1payouts/post/responses/400",
        "headers": [],
        "provider_code": "validation_error",
        "internal_code": "validation_error",
        "action": "reject"
      },
      {
        "status": 401,
        "role": "create",
        "pointer": "#/paths/~1payouts/post/responses/401",
        "headers": [],
        "provider_code": "unauthorized",
        "internal_code": "invalid_credentials",
        "action": "alert_block"
      },
      {
        "status": 402,
        "role": "create",
        "pointer": "#/paths/~1payouts/post/responses/402",
        "headers": [],
        "provider_code": "insufficient_balance",
        "internal_code": "insufficient_balance",
        "action": "retry"
      },
      {
        "status": 409,
        "role": "create",
        "pointer": "#/paths/~1payouts/post/responses/409",
        "headers": [],
        "provider_code": null,
        "internal_code": "duplicate",
        "action": "treat_as_success"
      },
      {
        "status": 422,
        "role": "create",
        "pointer": "#/paths/~1payouts/post/responses/422",
        "headers": [],
        "provider_code": "validation_error",
        "internal_code": "validation_error",
        "action": "reject"
      },
      {
        "status": 429,
        "role": "create",
        "pointer": "#/paths/~1payouts/post/responses/429",
        "headers": [
          "Retry-After"
        ],
        "provider_code": "rate_limit_exceeded",
        "internal_code": "rate_limit",
        "action": "retry_backoff"
      },
      {
        "status": 500,
        "role": "create",
        "pointer": "#/paths/~1payouts/post/responses/500",
        "headers": [],
        "provider_code": null,
        "internal_code": "internal_error",
        "action": "retry"
      },
      {
        "status": 401,
        "role": "status",
        "pointer": "#/paths/~1payouts~1{payout_id}/get/responses/401",
        "headers": [],
        "provider_code": "unauthorized",
        "internal_code": "invalid_credentials",
        "action": "alert_block"
      },
      {
        "status": 404,
        "role": "status",
        "pointer": "#/paths/~1payouts~1{payout_id}/get/responses/404",
        "headers": [],
        "provider_code": "not_found",
        "internal_code": "not_found",
        "action": "reject"
      },
      {
        "status": 409,
        "role": "cancel",
        "pointer": "#/paths/~1payouts~1{payout_id}~1cancel/post/responses/409",
        "headers": [],
        "provider_code": "invalid_status",
        "internal_code": "invalid_status",
        "action": "reject"
      }
    ],
    "http": {
      "400": "validation_error",
      "401": "invalid_credentials",
      "402": "insufficient_balance",
      "404": "not_found",
      "422": "validation_error",
      "429": "rate_limit",
      "500": "internal_error"
    },
    "codes": [
      "validation_error",
      "insufficient_balance",
      "recipient_not_found",
      "bank_unavailable",
      "amount_limit_exceeded",
      "rate_limit_exceeded",
      "internal_error"
    ],
    "retry_after_header": "Retry-After"
  },
  "webhook": {
    "endpoint": {
      "method": "post",
      "path": "/webhooks/payout",
      "operation_id": "payoutWebhook"
    },
    "source": "paths",
    "event_field": "event",
    "event_map": {
      "payout.completed": "approved",
      "payout.failed": "rejected",
      "payout.processing": "in_progress",
      "payout.cancelled": "rejected"
    },
    "id_field": [
      "payout_id"
    ],
    "status_field": [
      "status"
    ],
    "signature": {
      "header": "X-NovaPay-Signature",
      "algorithm": "sha256",
      "encoding": "hex",
      "payload": "raw_body",
      "scheme": "plain",
      "secret_key": "callback_secret"
    }
  },
  "amount": {
    "field": "amount",
    "path": [
      "amount"
    ],
    "unit": "minor",
    "multiplier": 100,
    "currencies": [
      "RUB"
    ],
    "currency_field": "currency",
    "type": "integer",
    "expr": "to_minor_units(operation.amount)",
    "minimum_major": 1000,
    "maximum_major": null
  },
  "fields": {
    "request": [
      {
        "provider_field": "amount",
        "path": [
          "amount"
        ],
        "source_expr": "to_minor_units(operation.amount)",
        "required": true,
        "required_if": null,
        "transform": "amount",
        "schema": {
          "type": "integer"
        },
        "confidence": 0.95,
        "requisite_type": null
      },
      {
        "provider_field": "currency",
        "path": [
          "currency"
        ],
        "source_expr": "operation.currency",
        "required": true,
        "required_if": null,
        "transform": null,
        "schema": {
          "type": "string",
          "enum": [
            "RUB"
          ]
        },
        "confidence": 0.95,
        "requisite_type": null
      },
      {
        "provider_field": "external_id",
        "path": [
          "external_id"
        ],
        "source_expr": "operation.id.to_s",
        "required": true,
        "required_if": null,
        "transform": "to_s",
        "schema": {
          "type": "string"
        },
        "confidence": 0.95,
        "requisite_type": null
      },
      {
        "provider_field": "type",
        "path": [
          "recipient",
          "type"
        ],
        "source_expr": "requisite_type",
        "required": true,
        "required_if": null,
        "transform": null,
        "schema": {
          "type": "string",
          "enum": [
            "sbp",
            "card"
          ]
        },
        "confidence": 0.95,
        "requisite_type": null
      },
      {
        "provider_field": "phone",
        "path": [
          "recipient",
          "phone"
        ],
        "source_expr": "operation.payout_requisite.dig(requisite_type, 'phone')",
        "required": true,
        "required_if": null,
        "transform": null,
        "schema": {
          "type": "string"
        },
        "confidence": 0.9,
        "requisite_type": null
      },
      {
        "provider_field": "bank_code",
        "path": [
          "recipient",
          "bank_code"
        ],
        "source_expr": "operation.payout_requisite.dig('sbp', 'bank_code')",
        "required": false,
        "required_if": {
          "field": "type",
          "equals": "sbp"
        },
        "transform": null,
        "schema": {
          "type": "string"
        },
        "confidence": 0.6,
        "requisite_type": "sbp"
      },
      {
        "provider_field": "bank_name",
        "path": [
          "recipient",
          "bank_name"
        ],
        "source_expr": "operation.payout_requisite.dig('sbp', 'bank_name')",
        "required": false,
        "required_if": null,
        "transform": null,
        "schema": {
          "type": "string"
        },
        "confidence": 0.9,
        "requisite_type": "sbp"
      },
      {
        "provider_field": "card_number",
        "path": [
          "recipient",
          "card_number"
        ],
        "source_expr": "operation.payout_requisite.dig('card', 'card_number')",
        "required": false,
        "required_if": {
          "field": "type",
          "equals": "card"
        },
        "transform": null,
        "schema": {
          "type": "string"
        },
        "confidence": 0.6,
        "requisite_type": "card"
      }
    ],
    "requisite_types": [
      "sbp",
      "card"
    ],
    "requisite_container": "recipient",
    "requisite_type_values": {
      "sbp": "sbp",
      "card": "card"
    },
    "headers": [
      {
        "provider_field": "Idempotency-Key",
        "path": [
          "Idempotency-Key"
        ],
        "source_expr": "operation.idempotency_key",
        "required": false,
        "required_if": null,
        "transform": null,
        "schema": {
          "type": "string"
        },
        "confidence": 0.9,
        "requisite_type": null
      }
    ]
  },
  "outputs": [
    ".//data/web/20260906-191850-8d9be3/out/novapay_service.rb",
    ".//data/web/20260906-191850-8d9be3/out/INTEGRATION.md",
    ".//data/web/20260906-191850-8d9be3/out/fixtures.json",
    ".//data/web/20260906-191850-8d9be3/out/novapay_service_spec.rb",
    ".//data/web/20260906-191850-8d9be3/out/novapay_extras.rb",
    ".//data/web/20260906-191850-8d9be3/out/mock_server.rb",
    ".//data/web/20260906-191850-8d9be3/out/report.txt"
  ],
  "warnings": [
    {
      "level": "info",
      "code": "outside_contract",
      "message": "POST /payouts/{payout_id}/cancel (cancelPayout) — generated as `cancel_request` helper",
      "pointer": "#/paths/~1payouts~1{payout_id}~1cancel/post",
      "hint": null
    },
    {
      "level": "info",
      "code": "outside_contract",
      "message": "GET /balance (getBalance) — generated as `fetch_balance` helper",
      "pointer": "#/paths/~1balance/get",
      "hint": null
    },
    {
      "level": "info",
      "code": "duplicate_as_success",
      "message": "HTTP 409 returns the success schema (PayoutResponse); treated as success",
      "pointer": null,
      "hint": "the service reads the payout from the body"
    },
    {
      "level": "warn",
      "code": "signature_encoding_assumed",
      "message": "X-NovaPay-Signature: encoding not stated; hex assumed",
      "pointer": null,
      "hint": "webhook.signature_encoding: hex|base64  (overrides.yml)"
    },
    {
      "level": "warn",
      "code": "conditional_required",
      "message": "recipient.bank_code: required only for type=sbp (from description)",
      "pointer": null,
      "hint": "fields.recipient.bank_code.required_if: { field: type, equals: sbp }  applied; verify"
    },
    {
      "level": "warn",
      "code": "conditional_required",
      "message": "recipient.card_number: required only for type=card (from description)",
      "pointer": null,
      "hint": "fields.recipient.card_number.required_if: { field: type, equals: card }  applied; verify"
    }
  ],
  "exit_code": 0
}
