{
  "openapi": "3.1.0",
  "info": {
    "title": "CryptoOctopus Payments and Payouts API",
    "version": "0.7.0-rc.1",
    "description": "Application-scoped HTTPS/JSON. Receiving and customer payouts require separate least-privilege API keys. KYC/account approval and platform payout controls apply."
  },
  "servers": [
    {
      "url": "http://127.0.0.1:3102",
      "description": "Local simulation; replace with your HTTPS deployment."
    }
  ],
  "security": [
    {
      "BearerKey": []
    }
  ],
  "paths": {
    "/v1/payments/{id}/operations": {
      "parameters": [{ "$ref": "#/components/parameters/PaymentId" }],
      "get": {
        "operationId": "getPaymentOperations",
        "summary": "Read application-scoped purchase snapshot, fulfillment, saved receipts and recovery requests",
        "responses": {
          "200": { "description": "Operation detail" },
          "404": { "description": "Missing or other application's payment" }
        }
      }
    },
    "/v1/payments/{id}/fulfillment": {
      "parameters": [{ "$ref": "#/components/parameters/PaymentId" }],
      "post": {
        "operationId": "acknowledgeFulfillment",
        "summary": "Record merchant assertion of service delivery; does not move money or activate external services",
        "parameters": [{ "$ref": "#/components/parameters/Idempotency" }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/Fulfillment" }
            }
          }
        },
        "responses": {
          "200": { "description": "Saved event or identical retry" },
          "400": { "description": "Invalid fields" },
          "403": { "description": "Receiving write scope required" },
          "409": {
            "description": "Stale revision, idempotency conflict, final delivery or unsettled payment"
          }
        }
      }
    },
    "/v1/payments/{id}/recovery": {
      "parameters": [{ "$ref": "#/components/parameters/PaymentId" }],
      "post": {
        "operationId": "requestPaymentRecovery",
        "summary": "Record a support request; claimed transfers are not credited",
        "parameters": [{ "$ref": "#/components/parameters/Idempotency" }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/RecoveryRequest" }
            }
          }
        },
        "responses": {
          "201": { "description": "Submitted request or identical retry" },
          "400": { "description": "Invalid reason or fields" },
          "409": { "description": "Idempotency conflict" }
        }
      }
    },
    "/v1/payments/{id}/documents/{documentId}": {
      "parameters": [
        { "$ref": "#/components/parameters/PaymentId" },
        {
          "name": "documentId",
          "in": "path",
          "required": true,
          "schema": { "type": "string", "format": "uuid" }
        }
      ],
      "get": {
        "operationId": "getPaymentDocument",
        "summary": "Read an immutable receipt/statement snapshot as JSON",
        "responses": {
          "200": {
            "description": "Saved document with revision; not a legal tax invoice"
          },
          "404": { "description": "Document not found in scoped payment" }
        }
      }
    },
    "/v1/payments/{id}/evidence": {
      "parameters": [{ "$ref": "#/components/parameters/PaymentId" }],
      "get": {
        "operationId": "getPaymentEvidence",
        "summary": "Export minimized payment evidence; not an executable migration package",
        "responses": {
          "200": {
            "description": "Evidence, checksum and gateway-only HMAC authentication; excludes customer order metadata and signing secrets"
          },
          "404": { "description": "Payment not found" }
        }
      }
    },
    "/v1/checkouts": {
      "post": {
        "operationId": "createCustomerChoiceCheckout",
        "summary": "Customer selects an offered token then a running network; selection locks before address creation",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": { "type": "string", "minLength": 8, "maxLength": 128 }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": ["amount", "currencies"],
                "properties": {
                  "amount": {
                    "type": "string",
                    "description": "Exact positive decimal, up to 6 decimal places. Same numeric amount in either offered token, no currency exchange."
                  },
                  "currencies": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 2,
                    "uniqueItems": true,
                    "items": { "type": "string", "enum": ["USDT", "USDC"] }
                  },
                  "reference": { "type": "string", "maxLength": 200 }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Choice id and checkoutUrl. Not yet a payable invoice. Unselected links expire after 24 hours."
          },
          "400": { "description": "Invalid input" },
          "401": { "description": "Invalid receiving API key" },
          "403": { "description": "Account/KYC/application/scope not allowed" },
          "409": {
            "description": "Idempotency conflict or no running network for a requested token"
          }
        }
      }
    },
    "/v1/networks": {
      "get": {
        "operationId": "listNetworks",
        "summary": "List configured tokens and decimals, not planned integrations",
        "responses": {
          "200": {
            "description": "Successful JSON response"
          },
          "400": {
            "description": "Invalid input or unsupported network/token"
          },
          "401": {
            "description": "Invalid/revoked API key"
          },
          "403": {
            "description": "Account/KYC/application/scope blocked"
          },
          "404": {
            "description": "Not found, including cross-application access"
          },
          "409": {
            "description": "Idempotency conflict"
          },
          "413": {
            "description": "Request exceeds 16 KiB"
          },
          "429": {
            "description": "Rate limit; retry with backoff"
          },
          "503": {
            "description": "Temporary failure; retry with same idempotency key"
          }
        }
      }
    },
    "/v1/payouts": {
      "get": {
        "operationId": "listCustomerPayouts",
        "summary": "List payouts for the payout key's application",
        "responses": {
          "200": { "description": "Application-isolated payout list" },
          "401": { "description": "Invalid payout API key" },
          "403": { "description": "Payout-read scope required" }
        }
      },
      "post": {
        "operationId": "createCustomerPayout",
        "summary": "Request a customer payout from the merchant balance",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": { "type": "string", "pattern": "^[A-Za-z0-9._-]{8,128}$" }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/PayoutRequest" }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Payout reserved and queued or awaiting approval"
          },
          "400": { "description": "Invalid input" },
          "401": { "description": "Invalid payout API key" },
          "403": { "description": "Payout-write scope required" },
          "409": {
            "description": "Policy, balance, risk, limit, or idempotency conflict"
          }
        }
      }
    },
    "/v1/payouts/{id}": {
      "get": {
        "operationId": "getCustomerPayout",
        "summary": "Read one application-isolated payout",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": { "type": "string", "format": "uuid" }
          }
        ],
        "responses": {
          "200": {
            "description": "Payout state and transaction hash when broadcast"
          },
          "404": {
            "description": "Not found, including cross-application access"
          }
        }
      }
    },
    "/v1/payments": {
      "post": {
        "operationId": "createPayment",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9._-]{8,128}$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PaymentRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful JSON response"
          },
          "201": {
            "description": "Created or identical replay, including checkoutUrl."
          },
          "400": {
            "description": "Invalid input or unsupported network/token"
          },
          "401": {
            "description": "Invalid/revoked API key"
          },
          "403": {
            "description": "Account/KYC/application/scope blocked"
          },
          "404": {
            "description": "Not found, including cross-application access"
          },
          "409": {
            "description": "Idempotency conflict"
          },
          "413": {
            "description": "Request exceeds 16 KiB"
          },
          "429": {
            "description": "Rate limit; retry with backoff"
          },
          "503": {
            "description": "Temporary failure; retry with same idempotency key"
          }
        }
      },
      "get": {
        "operationId": "listPayments",
        "parameters": [
          {
            "name": "before",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            },
            "description": "Use nextCursor; omit for newest 100."
          }
        ],
        "responses": {
          "200": {
            "description": "Successful JSON response"
          },
          "400": {
            "description": "Invalid input or unsupported network/token"
          },
          "401": {
            "description": "Invalid/revoked API key"
          },
          "403": {
            "description": "Account/KYC/application/scope blocked"
          },
          "404": {
            "description": "Not found, including cross-application access"
          },
          "409": {
            "description": "Idempotency conflict"
          },
          "413": {
            "description": "Request exceeds 16 KiB"
          },
          "429": {
            "description": "Rate limit; retry with backoff"
          },
          "503": {
            "description": "Temporary failure; retry with same idempotency key"
          }
        }
      }
    },
    "/v1/payments/{id}": {
      "get": {
        "operationId": "getPayment",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful JSON response"
          },
          "400": {
            "description": "Invalid input or unsupported network/token"
          },
          "401": {
            "description": "Invalid/revoked API key"
          },
          "403": {
            "description": "Account/KYC/application/scope blocked"
          },
          "404": {
            "description": "Not found, including cross-application access"
          },
          "409": {
            "description": "Idempotency conflict"
          },
          "413": {
            "description": "Request exceeds 16 KiB"
          },
          "429": {
            "description": "Rate limit; retry with backoff"
          },
          "503": {
            "description": "Temporary failure; retry with same idempotency key"
          }
        }
      }
    },
    "/v1/payments/{id}/receipt": {
      "get": {
        "operationId": "getReceipt",
        "summary": "Receipt or unpaid statement with merchant-supplied order/customer metadata",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful JSON response"
          },
          "400": {
            "description": "Invalid input or unsupported network/token"
          },
          "401": {
            "description": "Invalid/revoked API key"
          },
          "403": {
            "description": "Account/KYC/application/scope blocked"
          },
          "404": {
            "description": "Not found, including cross-application access"
          },
          "409": {
            "description": "Idempotency conflict"
          },
          "413": {
            "description": "Request exceeds 16 KiB"
          },
          "429": {
            "description": "Rate limit; retry with backoff"
          },
          "503": {
            "description": "Temporary failure; retry with same idempotency key"
          }
        }
      }
    }
  },
  "components": {
    "parameters": {
      "PaymentId": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": { "type": "string", "format": "uuid" }
      },
      "Idempotency": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": true,
        "schema": { "type": "string", "pattern": "^[A-Za-z0-9._-]{8,128}$" }
      }
    },
    "securitySchemes": {
      "BearerKey": {
        "type": "http",
        "scheme": "bearer"
      }
    },
    "schemas": {
      "Operation": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "industry",
          "businessModel",
          "productId",
          "productVersion",
          "purpose",
          "promise",
          "termsVersion"
        ],
        "properties": {
          "industry": {
            "type": "string",
            "description": "Valid industry catalog ID, for example trading. No new financial permission is granted."
          },
          "businessModel": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120
          },
          "productId": { "type": "string", "minLength": 1, "maxLength": 120 },
          "productVersion": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120
          },
          "purpose": { "type": "string", "minLength": 1, "maxLength": 120 },
          "promise": { "type": "string", "minLength": 1, "maxLength": 1500 },
          "termsVersion": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120
          },
          "deliveryDeadline": {
            "type": ["string", "null"],
            "format": "date-time",
            "description": "Optional target within next 366 days; informational, not a guaranteed deadline or scheduler override."
          }
        }
      },
      "Fulfillment": {
        "type": "object",
        "required": ["status", "revision", "reference", "note"],
        "properties": {
          "status": {
            "type": "string",
            "enum": ["pending", "delivered", "failed"]
          },
          "revision": { "type": "integer", "minimum": 0 },
          "reference": { "type": "string", "minLength": 1, "maxLength": 200 },
          "note": { "type": "string", "minLength": 1, "maxLength": 1000 }
        }
      },
      "RecoveryRequest": {
        "type": "object",
        "required": ["kind", "note"],
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "missing_payment",
              "partial_payment",
              "overpayment",
              "late_payment",
              "service_not_received",
              "refund_question"
            ]
          },
          "note": { "type": "string", "minLength": 1, "maxLength": 1500 },
          "transactionReference": { "type": "string", "maxLength": 200 }
        }
      },
      "Customer": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "id": {
            "type": "string",
            "maxLength": 100
          },
          "name": {
            "type": "string",
            "maxLength": 150
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "country": {
            "type": "string",
            "maxLength": 80
          }
        }
      },
      "Item": {
        "type": "object",
        "additionalProperties": false,
        "required": ["name", "quantity", "unitAmount"],
        "properties": {
          "sku": {
            "type": "string",
            "maxLength": 100
          },
          "name": {
            "type": "string",
            "maxLength": 200
          },
          "quantity": {
            "type": "integer",
            "minimum": 1,
            "maximum": 10000
          },
          "unitAmount": {
            "type": "string",
            "pattern": "^(0|[1-9][0-9]*)(\\.[0-9]+)?$",
            "description": "Exact decimal string. Must fit the configured token decimals."
          }
        }
      },
      "Order": {
        "type": "object",
        "additionalProperties": false,
        "required": ["orderId", "items"],
        "properties": {
          "orderId": {
            "type": "string",
            "maxLength": 200
          },
          "customer": {
            "$ref": "#/components/schemas/Customer"
          },
          "items": {
            "type": "array",
            "minItems": 1,
            "maxItems": 30,
            "items": {
              "$ref": "#/components/schemas/Item"
            }
          },
          "taxAmount": {
            "type": "string",
            "pattern": "^(0|[1-9][0-9]*)(\\.[0-9]+)?$",
            "description": "Exact decimal string. Must fit the configured token decimals."
          },
          "shippingAmount": {
            "type": "string",
            "pattern": "^(0|[1-9][0-9]*)(\\.[0-9]+)?$",
            "description": "Exact decimal string. Must fit the configured token decimals."
          },
          "discountAmount": {
            "type": "string",
            "pattern": "^(0|[1-9][0-9]*)(\\.[0-9]+)?$",
            "description": "Exact decimal string. Must fit the configured token decimals."
          }
        }
      },
      "PayoutRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "externalCustomerId",
          "network",
          "currency",
          "amount",
          "address"
        ],
        "properties": {
          "externalCustomerId": { "type": "string", "maxLength": 120 },
          "network": {
            "type": "string",
            "description": "Enabled payout network from GET /v1/networks."
          },
          "currency": { "type": "string", "enum": ["USDT", "USDC"] },
          "amount": {
            "type": "string",
            "pattern": "^(0|[1-9][0-9]*)(\\.[0-9]+)?$"
          },
          "address": { "type": "string", "maxLength": 200 },
          "reference": { "type": "string", "maxLength": 200 }
        }
      },
      "PaymentRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": ["network", "currency", "amount"],
        "properties": {
          "operation": { "$ref": "#/components/schemas/Operation" },
          "network": {
            "type": "string",
            "description": "Exact configured name from GET /v1/networks."
          },
          "currency": {
            "type": "string",
            "enum": ["USDT", "USDC"]
          },
          "amount": {
            "type": "string",
            "pattern": "^(0|[1-9][0-9]*)(\\.[0-9]+)?$",
            "description": "Exact decimal string. Must fit the configured token decimals."
          },
          "reference": {
            "type": "string",
            "maxLength": 200
          },
          "order": {
            "$ref": "#/components/schemas/Order"
          }
        }
      }
    }
  }
}
