{
  "openapi": "3.0.0",
  "paths": {
    "/.well-known/jwks.json": {
      "get": {
        "description": "Returns the public keys in JWKS format for JWT signature verification. Used by external services (e.g., Jack Henry jXchange) to verify signed JWTs.",
        "operationId": "JwksController_getJwks",
        "parameters": [],
        "responses": {
          "200": {
            "description": "JWKS containing public keys",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JwksResponseDto"
                }
              }
            }
          }
        },
        "summary": "Get JSON Web Key Set",
        "tags": [
          "JWKS"
        ]
      }
    },
    "/api/v1/api/v1/compliance/verifications": {
      "post": {
        "operationId": "ComplianceController_createVerification",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateVerificationDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Verification created successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VerificationResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request data"
          },
          "401": {
            "description": "Invalid or missing API key"
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Create a new verification request",
        "tags": [
          "compliance"
        ]
      }
    },
    "/api/v1/api/v1/compliance/verifications/check": {
      "post": {
        "operationId": "ComplianceController_checkVerification",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CheckVerificationDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Verification status retrieved",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VerificationResponseDto"
                }
              }
            }
          },
          "404": {
            "description": "Verification not found"
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Check verification status",
        "tags": [
          "compliance"
        ]
      }
    },
    "/api/v1/api/v1/compliance/verifications/{id}": {
      "get": {
        "operationId": "ComplianceController_getVerification",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Verification found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VerificationResponseDto"
                }
              }
            }
          },
          "404": {
            "description": "Verification not found"
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Get verification by ID",
        "tags": [
          "compliance"
        ]
      }
    },
    "/api/v1/api/v1/compliance/webhooks/persona": {
      "post": {
        "operationId": "ComplianceController_handlePersonaWebhook",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Webhook processed successfully"
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Webhook endpoint for Persona verification updates",
        "tags": [
          "compliance"
        ]
      }
    },
    "/api/v1/auth/api-key": {
      "post": {
        "description": "Generate a new API key for a platform. Requires an existing admin-scoped API key. The new API key is only shown once.",
        "operationId": "AuthController_createApiKey",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateApiKeyDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "API key created successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKeyResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Bad request"
          },
          "401": {
            "description": "Missing or invalid API key"
          },
          "403": {
            "description": "Caller lacks admin:platform scope"
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Create new API key",
        "tags": [
          "Authentication"
        ]
      }
    },
    "/api/v1/auth/api-key/regenerate": {
      "post": {
        "description": "Generate a new API key for an existing platform. The old key will be invalidated.",
        "operationId": "AuthController_regenerateApiKey",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RegenerateApiKeyDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "API key regenerated successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKeyResponseDto"
                }
              }
            }
          },
          "404": {
            "description": "Platform not found"
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Regenerate API key",
        "tags": [
          "Authentication"
        ]
      }
    },
    "/api/v1/auth/platform/me": {
      "get": {
        "description": "Get platform details for the authenticated API key",
        "operationId": "AuthController_getCurrentPlatform",
        "parameters": [
          {
            "name": "x-api-key",
            "required": true,
            "in": "header",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-API-Key",
            "in": "header",
            "description": "API key",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Current platform details",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Platform"
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing API key"
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Get current platform",
        "tags": [
          "Authentication"
        ]
      }
    },
    "/api/v1/auth/platform/{id}": {
      "get": {
        "description": "Retrieve platform information by ID",
        "operationId": "AuthController_getPlatform",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Platform found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Platform"
                }
              }
            }
          },
          "404": {
            "description": "Platform not found"
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Get platform details",
        "tags": [
          "Authentication"
        ]
      },
      "put": {
        "description": "Update platform settings",
        "operationId": "AuthController_updatePlatform",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdatePlatformDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Platform updated successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Platform"
                }
              }
            }
          },
          "404": {
            "description": "Platform not found"
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Update platform",
        "tags": [
          "Authentication"
        ]
      },
      "delete": {
        "description": "Deactivate a platform (soft delete)",
        "operationId": "AuthController_deactivatePlatform",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Platform deactivated successfully"
          },
          "404": {
            "description": "Platform not found"
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Deactivate platform",
        "tags": [
          "Authentication"
        ]
      }
    },
    "/api/v1/auth/platforms": {
      "get": {
        "description": "Get all platforms (admin only)",
        "operationId": "AuthController_listPlatforms",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Platforms retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Platform"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "List all platforms",
        "tags": [
          "Authentication"
        ]
      }
    },
    "/api/v1/auth/verify": {
      "post": {
        "description": "Check if an API key is valid and active",
        "operationId": "AuthController_verifyApiKey",
        "parameters": [
          {
            "name": "x-api-key",
            "required": true,
            "in": "header",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-API-Key",
            "in": "header",
            "description": "API key to verify",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Verification result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VerifyApiKeyResponseDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Verify API key",
        "tags": [
          "Authentication"
        ]
      }
    },
    "/api/v1/controls/verify-2fa": {
      "post": {
        "description": "Verify SMS code sent via TwoFactorAuth control",
        "operationId": "ControlsController_verify2FA",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VerifyCodeDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Verification result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VerificationResponseDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Verify 2FA code",
        "tags": [
          "Controls"
        ]
      }
    },
    "/api/v1/controls/verify-twilio-sms": {
      "post": {
        "description": "Verify SMS code sent via TwilioSMS control using Twilio Verify",
        "operationId": "ControlsController_verifyTwilioSMS",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VerifyCodeDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Verification result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VerificationResponseDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Verify Twilio SMS code",
        "tags": [
          "Controls"
        ]
      }
    },
    "/api/v1/disputes": {
      "get": {
        "operationId": "DisputeController_getDisputes",
        "parameters": [
          {
            "name": "status",
            "required": false,
            "in": "query",
            "description": "Filter by dispute status",
            "schema": {
              "enum": [
                "open",
                "under_review",
                "pending_votes",
                "in_arbitration",
                "resolved"
              ],
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Disputes retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/DisputeResponseDto"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get all disputes for platform",
        "tags": [
          "disputes"
        ]
      },
      "post": {
        "operationId": "DisputeController_createDispute",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateDisputeDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Dispute created successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DisputeResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request data or transaction not disputable"
          },
          "404": {
            "description": "Transaction not found"
          },
          "409": {
            "description": "Dispute already exists for this transaction"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Create a new dispute for a transaction",
        "tags": [
          "disputes"
        ]
      }
    },
    "/api/v1/disputes/{id}": {
      "get": {
        "operationId": "DisputeController_getDispute",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Dispute found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DisputeResponseDto"
                }
              }
            }
          },
          "404": {
            "description": "Dispute not found"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get dispute by ID",
        "tags": [
          "disputes"
        ]
      }
    },
    "/api/v1/disputes/{id}/escalate": {
      "post": {
        "operationId": "DisputeController_escalateDispute",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Dispute escalated successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DisputeResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Cannot escalate resolved dispute or already at highest tier"
          },
          "404": {
            "description": "Dispute not found"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Escalate dispute to next tier",
        "tags": [
          "disputes"
        ]
      }
    },
    "/api/v1/disputes/{id}/evidence": {
      "post": {
        "operationId": "DisputeController_submitEvidence",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SubmitEvidenceDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Evidence submitted successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EvidenceResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Cannot submit evidence to resolved dispute"
          },
          "404": {
            "description": "Dispute not found"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Submit evidence for a dispute",
        "tags": [
          "disputes"
        ]
      }
    },
    "/api/v1/disputes/{id}/resolve": {
      "post": {
        "operationId": "DisputeController_resolveDispute",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ResolveDisputeDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Dispute resolved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DisputeResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Dispute already resolved or invalid resolution data"
          },
          "404": {
            "description": "Dispute not found"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Resolve a dispute",
        "tags": [
          "disputes"
        ]
      }
    },
    "/api/v1/health": {
      "get": {
        "description": "Returns detailed health status of all system components including database, cache, blockchain, and external services",
        "operationId": "HealthController_check",
        "parameters": [],
        "responses": {
          "200": {
            "description": "System is healthy",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HealthCheckDto"
                }
              }
            }
          },
          "503": {
            "description": "System is unhealthy",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HealthCheckDto"
                }
              }
            }
          }
        },
        "summary": "Complete health check",
        "tags": [
          "Health"
        ]
      }
    },
    "/api/v1/health/live": {
      "get": {
        "description": "Kubernetes liveness probe - checks if the application is running. Returns 200 if alive, 503 if dead.",
        "operationId": "HealthController_liveness",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Application is alive",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LivenessCheckDto"
                }
              }
            }
          },
          "503": {
            "description": "Application is not responding"
          }
        },
        "summary": "Liveness probe",
        "tags": [
          "Health"
        ]
      }
    },
    "/api/v1/health/ready": {
      "get": {
        "description": "Kubernetes readiness probe - checks if application can accept traffic. Validates critical dependencies.",
        "operationId": "HealthController_readiness",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Application is ready to accept traffic",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReadinessCheckDto"
                }
              }
            }
          },
          "503": {
            "description": "Application is not ready",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReadinessCheckDto"
                }
              }
            }
          }
        },
        "summary": "Readiness probe",
        "tags": [
          "Health"
        ]
      }
    },
    "/api/v1/oauth/clients": {
      "get": {
        "description": "List all OAuth clients for a platform",
        "operationId": "OAuthController_listClients",
        "parameters": [
          {
            "name": "platformId",
            "required": true,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "workspaceId",
            "required": true,
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of OAuth clients",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/OAuthClientInfoDto"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "List OAuth clients",
        "tags": [
          "OAuth 2.0"
        ]
      },
      "post": {
        "description": "Register a new OAuth client for a platform. The client secret is only shown once.",
        "operationId": "OAuthController_createClient",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateOAuthClientDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "OAuth client created successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateOAuthClientResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request or invalid scopes"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Create OAuth client",
        "tags": [
          "OAuth 2.0"
        ]
      }
    },
    "/api/v1/oauth/clients/{clientId}": {
      "get": {
        "operationId": "OAuthController_getClient",
        "parameters": [
          {
            "name": "clientId",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OAuth client details",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthClientInfoDto"
                }
              }
            }
          },
          "404": {
            "description": "Client not found"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get OAuth client details",
        "tags": [
          "OAuth 2.0"
        ]
      },
      "delete": {
        "description": "Permanently delete an OAuth client and all its tokens",
        "operationId": "OAuthController_deleteClient",
        "parameters": [
          {
            "name": "clientId",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "OAuth client deleted successfully"
          },
          "404": {
            "description": "Client not found"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Delete OAuth client",
        "tags": [
          "OAuth 2.0"
        ]
      },
      "patch": {
        "description": "Update OAuth client settings. Deactivating a client will revoke all its tokens.",
        "operationId": "OAuthController_updateClient",
        "parameters": [
          {
            "name": "clientId",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateOAuthClientDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OAuth client updated successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthClientInfoDto"
                }
              }
            }
          },
          "404": {
            "description": "Client not found"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Update OAuth client",
        "tags": [
          "OAuth 2.0"
        ]
      }
    },
    "/api/v1/oauth/clients/{clientId}/revoke-all-tokens": {
      "post": {
        "description": "Revoke all active access tokens for a client",
        "operationId": "OAuthController_revokeAllClientTokens",
        "parameters": [
          {
            "name": "clientId",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "All tokens revoked"
          },
          "404": {
            "description": "Client not found"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Revoke all client tokens",
        "tags": [
          "OAuth 2.0"
        ]
      }
    },
    "/api/v1/oauth/clients/{clientId}/stats": {
      "get": {
        "description": "Get token usage statistics for a client",
        "operationId": "OAuthController_getClientStats",
        "parameters": [
          {
            "name": "clientId",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Token statistics"
          },
          "404": {
            "description": "Client not found"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get client token statistics",
        "tags": [
          "OAuth 2.0"
        ]
      }
    },
    "/api/v1/oauth/revoke": {
      "post": {
        "description": "Revoke an active access token by its JWT ID (jti)",
        "operationId": "OAuthController_revoke",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RevokeTokenDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Token revoked successfully"
          },
          "404": {
            "description": "Token not found"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Revoke access token",
        "tags": [
          "OAuth 2.0"
        ]
      }
    },
    "/api/v1/oauth/scopes": {
      "get": {
        "description": "Get the catalog of available OAuth scopes that can be assigned to clients",
        "operationId": "OAuthController_listScopes",
        "parameters": [],
        "responses": {
          "200": {
            "description": "List of available scopes",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/OAuthScope"
                  }
                }
              }
            }
          }
        },
        "summary": "List available OAuth scopes",
        "tags": [
          "OAuth 2.0"
        ]
      }
    },
    "/api/v1/oauth/token": {
      "post": {
        "description": "Generate access token using Client Credentials flow (RFC 6749)",
        "operationId": "OAuthController_token",
        "parameters": [],
        "requestBody": {
          "required": true,
          "description": "OAuth token request",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OAuthTokenRequestDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Access token generated successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthTokenResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request or unsupported grant type"
          },
          "401": {
            "description": "Invalid client credentials"
          }
        },
        "summary": "OAuth 2.0 Token Endpoint",
        "tags": [
          "OAuth 2.0"
        ]
      }
    },
    "/api/v1/platform/config": {
      "get": {
        "description": "Returns contract deployments, templates, and settings for the authenticated platform",
        "operationId": "PlatformController_getConfig",
        "parameters": [
          {
            "name": "network",
            "required": false,
            "in": "query",
            "description": "Filter by specific network (e.g., base, base-sepolia)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "includeTemplate",
            "required": false,
            "in": "query",
            "description": "Include full template details (default: true)",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Platform configuration returned successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "platform": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "name": {
                          "type": "string"
                        }
                      }
                    },
                    "contracts": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "network": {
                            "type": "string"
                          },
                          "address": {
                            "type": "string"
                          },
                          "isShared": {
                            "type": "boolean"
                          },
                          "templateId": {
                            "type": "string"
                          },
                          "template": {
                            "type": "object"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing OAuth token"
          },
          "403": {
            "description": "Insufficient scopes"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get platform configuration",
        "tags": [
          "Platform"
        ]
      }
    },
    "/api/v1/sentry-test": {
      "get": {
        "description": "Triggers various types of Sentry events for testing. Use query parameter \"type\" to select test type: error, log, or span.",
        "operationId": "SentryTestController_testSentry",
        "parameters": [
          {
            "name": "type",
            "required": false,
            "in": "query",
            "description": "Type of Sentry test to run",
            "schema": {
              "enum": [
                "error",
                "log",
                "span"
              ],
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Test completed successfully (log or span)"
          },
          "500": {
            "description": "Test error thrown (error type)"
          }
        },
        "summary": "Test Sentry integration",
        "tags": [
          "Health"
        ]
      }
    },
    "/api/v1/transactions": {
      "get": {
        "operationId": "TransactionController_findAll",
        "parameters": [
          {
            "name": "page",
            "required": false,
            "in": "query",
            "description": "Page number",
            "schema": {
              "minimum": 1,
              "default": 1,
              "type": "number"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Items per page",
            "schema": {
              "minimum": 1,
              "maximum": 100,
              "default": 10,
              "type": "number"
            }
          },
          {
            "name": "status",
            "required": false,
            "in": "query",
            "description": "Filter by status",
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "risk_review",
                "pending_user_action",
                "escrowed",
                "in_review",
                "disputed",
                "completed",
                "refunded",
                "failed",
                "rescinded"
              ]
            }
          },
          {
            "name": "fromAddress",
            "required": false,
            "in": "query",
            "description": "Filter by sender blockchain address",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "toAddress",
            "required": false,
            "in": "query",
            "description": "Filter by receiver blockchain address",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "dateFrom",
            "required": false,
            "in": "query",
            "description": "Filter by date from (ISO 8601)",
            "schema": {
              "example": "2025-01-01T00:00:00Z",
              "type": "string"
            }
          },
          {
            "name": "dateTo",
            "required": false,
            "in": "query",
            "description": "Filter by date to (ISO 8601)",
            "schema": {
              "example": "2025-12-31T23:59:59Z",
              "type": "string"
            }
          },
          {
            "name": "customerId",
            "required": false,
            "in": "query",
            "description": "Filter by customer ID",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "walletAddress",
            "required": false,
            "in": "query",
            "description": "Filter by wallet address (matches fromAddress OR toAddress)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of transactions"
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "List transactions with filters",
        "tags": [
          "Transactions"
        ]
      },
      "post": {
        "operationId": "TransactionController_create",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateTransactionDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Transaction created successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Transaction"
                }
              }
            }
          },
          "409": {
            "description": "Duplicate transaction detected within time window"
          },
          "429": {
            "description": "Transaction rate limit exceeded (max 10/minute per sender)"
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Create a new transaction",
        "tags": [
          "Transactions"
        ]
      }
    },
    "/api/v1/transactions/at-risk": {
      "get": {
        "operationId": "TransactionController_getAtRiskStatistics",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Per-status (escrowed/in_review/pending_user_action/disputed) count, total amount, oldest age, and >24h/>72h aged counts"
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Money-at-risk: count + age of funds stuck in non-terminal states",
        "tags": [
          "Transactions"
        ]
      }
    },
    "/api/v1/transactions/batch": {
      "get": {
        "description": "Returns a paginated list of multi-recipient batches scoped to the calling platform. Legs are not embedded — fetch a single batch via GET /transactions/batch/:id for that.",
        "operationId": "TransactionController_findAllBatches",
        "parameters": [
          {
            "name": "page",
            "required": false,
            "in": "query",
            "description": "Page number",
            "schema": {
              "minimum": 1,
              "default": 1,
              "type": "number"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Items per page",
            "schema": {
              "minimum": 1,
              "maximum": 100,
              "default": 10,
              "type": "number"
            }
          },
          {
            "name": "status",
            "required": false,
            "in": "query",
            "description": "Filter by batch status",
            "schema": {
              "type": "string",
              "enum": [
                "pending_user_action",
                "submitted",
                "completed",
                "failed"
              ]
            }
          },
          {
            "name": "fromAddress",
            "required": false,
            "in": "query",
            "description": "Filter by sender blockchain address",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "dateFrom",
            "required": false,
            "in": "query",
            "description": "Filter by date from (ISO 8601)",
            "schema": {
              "example": "2026-01-01T00:00:00Z",
              "type": "string"
            }
          },
          {
            "name": "dateTo",
            "required": false,
            "in": "query",
            "description": "Filter by date to (ISO 8601)",
            "schema": {
              "example": "2026-12-31T23:59:59Z",
              "type": "string"
            }
          },
          {
            "name": "customerId",
            "required": false,
            "in": "query",
            "description": "Filter by customer ID",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of batches"
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "List transaction batches with filters",
        "tags": [
          "Transactions"
        ]
      },
      "post": {
        "description": "Creates a parent batch row plus one Transaction leg per recipient in a single DB transaction. The client (e.g., SafeSend) then signs and submits `WorkspaceEscrow.batchCreateEscrow` with each leg's UUID as the on-chain `transactionId`. Per-leg `EscrowCreated` events flow through the existing event monitor and transition each leg from PENDING_USER_ACTION to ESCROWED individually.",
        "operationId": "TransactionController_createBatch",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateTransactionBatchDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Batch created successfully (legs included in `legs`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TransactionBatch"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request — empty/oversized `legs`, malformed addresses, unsupported `signerType`, or template validation failure"
          },
          "403": {
            "description": "Customer approval required or sender not approved"
          },
          "429": {
            "description": "Transaction rate limit exceeded"
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Create a multi-recipient transaction batch",
        "tags": [
          "Transactions"
        ]
      }
    },
    "/api/v1/transactions/batch/{id}": {
      "get": {
        "description": "Returns the parent batch row plus all child Transaction legs. Each leg carries its own status, escrowId, and per-recipient metadata; per-leg listing of just the legs is also reachable via GET /transactions?batchId=…",
        "operationId": "TransactionController_findBatch",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Batch details with legs",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TransactionBatch"
                }
              }
            }
          },
          "404": {
            "description": "Batch not found"
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Get a transaction batch by ID",
        "tags": [
          "Transactions"
        ]
      }
    },
    "/api/v1/transactions/batch/{id}/cancel": {
      "post": {
        "description": "Marks a batch FAILED and propagates the same status to every leg. Allowed only while the batch is in `pending_user_action` (the user hasn't signed `batchCreateEscrow` yet, or is parked awaiting verification). Once the on-chain tx is in flight, use per-leg refund/dispute endpoints instead. Idempotent on already-FAILED batches. Fires a `transaction.batch.failed` webhook on success.",
        "operationId": "TransactionController_cancelBatch",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CancelBatchDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Batch cancelled (or was already FAILED — idempotent)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TransactionBatch"
                }
              }
            }
          },
          "400": {
            "description": "Batch is in a state that cannot be cancelled (submitted / completed)"
          },
          "404": {
            "description": "Batch not found"
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Manually cancel an unsubmitted batch",
        "tags": [
          "Transactions"
        ]
      }
    },
    "/api/v1/transactions/batch/{id}/submit-escrow-hash": {
      "post": {
        "description": "Stores the shared on-chain transaction hash on the batch + every leg, and advances the batch to `submitted`. Per-leg `EscrowCreated` events drive each leg from `pending_user_action` to `escrowed` independently via the existing event monitor.",
        "operationId": "TransactionController_submitBatchEscrowHash",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SubmitBatchEscrowHashDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Hash recorded, batch advanced to submitted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TransactionBatch"
                }
              }
            }
          },
          "400": {
            "description": "Batch is in a non-submittable state (already terminal, or still awaiting verification), or the request is for a custodial batch"
          },
          "404": {
            "description": "Batch not found"
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Record the on-chain batchCreateEscrow transaction hash",
        "tags": [
          "Transactions"
        ]
      }
    },
    "/api/v1/transactions/batch/{id}/verify-2fa": {
      "post": {
        "description": "Accepts the verification code the sender received and clears the batch's `awaitingControlVerification` flag once every PRE_ESCROW verification control resolves.",
        "operationId": "TransactionController_verifyBatch2FA",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Verify2FADto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Code accepted, batch updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TransactionBatch"
                }
              }
            }
          },
          "400": {
            "description": "Batch not awaiting verification, no pending 2FA control, or invalid/expired code"
          },
          "404": {
            "description": "Batch not found"
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Verify a 2FA code against a pending batch control",
        "tags": [
          "Transactions"
        ]
      }
    },
    "/api/v1/transactions/fee-preview": {
      "post": {
        "description": "Calculate and preview the platform fee before creating a transaction",
        "operationId": "TransactionController_previewFee",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FeePreviewRequestDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Fee preview calculated successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeePreviewResponseDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Preview platform fee for a transaction",
        "tags": [
          "Transactions"
        ]
      }
    },
    "/api/v1/transactions/quote": {
      "post": {
        "description": "Returns a 120-second-TTL quote with platform fee, workspace fee, gas pass-through (if applicable), and total charged. Pass the returned `quoteId` to `POST /transactions` to lock the rate.",
        "operationId": "TransactionController_createQuote",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateQuoteRequestDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Quote created successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QuoteResponseDto"
                }
              }
            }
          },
          "404": {
            "description": "Workspace has no ACTIVE PricingPlan / WorkspaceFeeSchedule. Operator intervention required."
          },
          "422": {
            "description": "Gas-cap exceeded and PricingPlan.onGasCapBreach=BLOCK. The caller can retry later if gas drops."
          },
          "503": {
            "description": "Transient: gas oracle / FX / coinbax-core unreachable. Retry."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Create a fee quote for a pending transaction",
        "tags": [
          "Transactions"
        ]
      }
    },
    "/api/v1/transactions/stats": {
      "get": {
        "operationId": "TransactionController_getStatistics",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Transaction statistics"
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Get transaction statistics",
        "tags": [
          "Transactions"
        ]
      }
    },
    "/api/v1/transactions/template-config": {
      "get": {
        "description": "Returns public-safe summaries of each active control on a template. Used by pre-confirmation surfaces (e.g. SafeSend) to render \"What protects your send\" panels and to enforce client-side AmountLimit min/max. Sanitized: only a hand-picked subset of each control config is returned — policy/implementation fields are NOT exposed.",
        "operationId": "TransactionController_getTemplateConfig",
        "parameters": [
          {
            "name": "templateId",
            "required": true,
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Template config retrieved",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TemplateConfigResponseDto"
                }
              }
            }
          },
          "404": {
            "description": "Template not found"
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Get sanitized template + controls config for a template ID",
        "tags": [
          "Transactions"
        ]
      }
    },
    "/api/v1/transactions/{id}": {
      "get": {
        "operationId": "TransactionController_findOne",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Transaction details",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Transaction"
                }
              }
            }
          },
          "404": {
            "description": "Transaction not found"
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Get transaction by ID",
        "tags": [
          "Transactions"
        ]
      }
    },
    "/api/v1/transactions/{id}/authorize-release": {
      "post": {
        "description": "Runs the PRE_RELEASE controls and records the aggregate result on-chain for an external-wallet (SafeSend) COINBAX escrow, so the sender wallet (releaseEarly / releaseAfterDelay) can release without the escrow's PRE_RELEASE gate reverting. Call this immediately BEFORE the on-chain release write. An early (pre-window) release waives the time-delay hold; a required non-time control failure auto-rejects the escrow and refunds the sender (do not then attempt an on-chain release).",
        "operationId": "TransactionController_authorizeRelease",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AuthorizeReleaseDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Release authorized (PRE_RELEASE attested Passed), or the escrow was rejected + refunded because a required control failed (see `rejected`)."
          },
          "400": {
            "description": "Not an external-wallet COINBAX escrow, not in ESCROWED status, or missing on-chain escrow id"
          },
          "404": {
            "description": "Transaction not found"
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Authorize release of an external-wallet escrow",
        "tags": [
          "Transactions"
        ]
      }
    },
    "/api/v1/transactions/{id}/bit-status": {
      "get": {
        "operationId": "TransactionController_getBitStatus",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Bit execution status retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ControlStatusResponseDto"
                }
              }
            }
          },
          "404": {
            "description": "Transaction not found"
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Get bit execution status for transaction",
        "tags": [
          "Transactions"
        ]
      }
    },
    "/api/v1/transactions/{id}/cancel": {
      "post": {
        "description": "Marks a transaction in PENDING_USER_ACTION as FAILED with userCancelled=true. No on-chain action — funds were never escrowed. Use this when the user rejects the wallet popup and does not intend to retry; rejects with 400 if the transaction has already advanced to ESCROWED (use /refund or /rescind instead) or is already terminal.",
        "operationId": "TransactionController_cancelPending",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CancelPendingTransactionDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Transaction cancelled successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Transaction"
                }
              }
            }
          },
          "400": {
            "description": "Transaction not in PENDING_USER_ACTION, or already has an on-chain escrow address"
          },
          "404": {
            "description": "Transaction not found"
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Cancel pre-escrow transaction the user never signed for",
        "tags": [
          "Transactions"
        ]
      }
    },
    "/api/v1/transactions/{id}/complete": {
      "post": {
        "operationId": "TransactionController_complete",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CompleteTransactionDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Transaction completed successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Transaction"
                }
              }
            }
          },
          "400": {
            "description": "Invalid transaction state for completion"
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Complete transaction and release funds",
        "tags": [
          "Transactions"
        ]
      }
    },
    "/api/v1/transactions/{id}/refund": {
      "post": {
        "operationId": "TransactionController_refund",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RefundTransactionDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Transaction refunded successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Transaction"
                }
              }
            }
          },
          "400": {
            "description": "Invalid transaction state for refund"
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Refund transaction",
        "tags": [
          "Transactions"
        ]
      }
    },
    "/api/v1/transactions/{id}/rescind": {
      "post": {
        "description": "Allows the sender to cancel a transaction during the Payment Hold Period and have funds returned to their wallet. Only available if the TimeDelay control has allowSenderRescind enabled.",
        "operationId": "TransactionController_rescind",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RescindTransactionDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Transaction rescinded successfully, funds returned to sender",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Transaction"
                }
              }
            }
          },
          "400": {
            "description": "Transaction cannot be rescinded (not in ESCROWED status, rescind not enabled, or rescind window expired)"
          },
          "403": {
            "description": "Only the sender can rescind a transaction"
          },
          "404": {
            "description": "Transaction not found"
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Rescind transaction during hold period (sender only)",
        "tags": [
          "Transactions"
        ]
      }
    },
    "/api/v1/transactions/{id}/resend-2fa": {
      "post": {
        "operationId": "TransactionController_resend2FA",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "2FA code resent successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string",
                      "example": "2FA code resent successfully"
                    },
                    "expiresIn": {
                      "type": "number",
                      "example": 300
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Transaction not awaiting 2FA verification"
          },
          "404": {
            "description": "Transaction not found"
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Resend 2FA verification code",
        "tags": [
          "Transactions"
        ]
      }
    },
    "/api/v1/transactions/{id}/retry-bit/{bitId}": {
      "post": {
        "operationId": "TransactionController_retryBit",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "bitId",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Bit execution retried successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Transaction"
                }
              }
            }
          },
          "400": {
            "description": "Invalid bit ID or bit cannot be retried"
          },
          "404": {
            "description": "Transaction or bit not found"
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Retry a failed bit execution",
        "tags": [
          "Transactions"
        ]
      }
    },
    "/api/v1/transactions/{id}/stream": {
      "get": {
        "description": "Opens an SSE stream for real-time transaction and control execution updates",
        "operationId": "TransactionStreamController_streamUpdates",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "SSE stream opened successfully"
          },
          "404": {
            "description": "Transaction not found"
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Subscribe to real-time transaction updates",
        "tags": [
          "Transaction Stream"
        ]
      }
    },
    "/api/v1/transactions/{id}/submit-escrow-hash": {
      "post": {
        "description": "After the user deposits USDC to the escrow contract via MetaMask, submit the transaction hash and escrow ID to confirm the escrow was created",
        "operationId": "TransactionController_submitEscrowHash",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SubmitEscrowHashDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Escrow hash submitted and verified successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Transaction"
                }
              }
            }
          },
          "400": {
            "description": "Invalid transaction state or escrow verification failed"
          },
          "404": {
            "description": "Transaction not found"
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Submit escrow transaction hash for external wallet (Transmitter) transactions",
        "tags": [
          "Transactions"
        ]
      }
    },
    "/api/v1/transactions/{id}/submit-hash": {
      "post": {
        "description": "After sending USDC from your wallet, submit the transaction hash to complete the transaction",
        "operationId": "TransactionController_submitTransactionHash",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SubmitTransactionHashDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Transaction hash submitted and verified successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Transaction"
                }
              }
            }
          },
          "400": {
            "description": "Invalid transaction state or hash verification failed"
          },
          "404": {
            "description": "Transaction not found"
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Submit blockchain transaction hash for RAW transaction",
        "tags": [
          "Transactions"
        ]
      }
    },
    "/api/v1/transactions/{id}/verify-2fa": {
      "post": {
        "operationId": "TransactionController_verify2FA",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Verify2FADto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "2FA verified successfully, transaction can proceed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Transaction"
                }
              }
            }
          },
          "400": {
            "description": "Invalid or expired verification code"
          },
          "404": {
            "description": "Transaction not found"
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Verify 2FA code for transaction",
        "tags": [
          "Transactions"
        ]
      }
    },
    "/api/v1/verify/{transactionId}": {
      "get": {
        "operationId": "VerificationController_getVerificationInfo",
        "parameters": [
          {
            "name": "transactionId",
            "required": true,
            "in": "path",
            "description": "Transaction UUID from SMS link",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Verification info returned"
          },
          "404": {
            "description": "Transaction not found"
          }
        },
        "summary": "Get verification info for a transaction (public)",
        "tags": [
          "Verification"
        ]
      }
    },
    "/api/v1/verify/{transactionId}/resend": {
      "post": {
        "operationId": "VerificationController_resendCode",
        "parameters": [
          {
            "name": "transactionId",
            "required": true,
            "in": "path",
            "description": "Transaction UUID",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Code resent successfully"
          },
          "429": {
            "description": "Rate limited"
          }
        },
        "summary": "Resend verification code (public)",
        "tags": [
          "Verification"
        ]
      }
    },
    "/api/v1/verify/{transactionId}/verify": {
      "post": {
        "operationId": "VerificationController_verifyCode",
        "parameters": [
          {
            "name": "transactionId",
            "required": true,
            "in": "path",
            "description": "Transaction UUID",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VerifyCodeDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Code verified successfully"
          },
          "400": {
            "description": "Invalid code"
          },
          "404": {
            "description": "Transaction not found"
          }
        },
        "summary": "Verify SMS code (public)",
        "tags": [
          "Verification"
        ]
      }
    },
    "/api/v1/webhooks": {
      "get": {
        "operationId": "WebhookController_list",
        "parameters": [
          {
            "name": "isActive",
            "required": true,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "eventType",
            "required": true,
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of webhook subscriptions"
          }
        },
        "security": [
          {
            "X-API-Key": []
          }
        ],
        "summary": "List webhook subscriptions",
        "tags": [
          "webhooks"
        ]
      },
      "post": {
        "operationId": "WebhookController_create",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateWebhookSubscriptionDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Webhook subscription created successfully"
          },
          "400": {
            "description": "Invalid event types"
          },
          "401": {
            "description": "Unauthorized"
          }
        },
        "security": [
          {
            "X-API-Key": []
          }
        ],
        "summary": "Create webhook subscription",
        "tags": [
          "webhooks"
        ]
      }
    },
    "/api/v1/webhooks/events/types": {
      "get": {
        "operationId": "WebhookController_getEventTypes",
        "parameters": [],
        "responses": {
          "200": {
            "description": "List of available event types"
          }
        },
        "security": [
          {
            "X-API-Key": []
          }
        ],
        "summary": "Get available webhook event types",
        "tags": [
          "webhooks"
        ]
      }
    },
    "/api/v1/webhooks/{id}": {
      "get": {
        "operationId": "WebhookController_get",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Subscription ID",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Webhook subscription details"
          },
          "404": {
            "description": "Subscription not found"
          }
        },
        "security": [
          {
            "X-API-Key": []
          }
        ],
        "summary": "Get webhook subscription",
        "tags": [
          "webhooks"
        ]
      },
      "delete": {
        "operationId": "WebhookController_delete",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Subscription ID",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Webhook subscription deleted"
          },
          "404": {
            "description": "Subscription not found"
          }
        },
        "security": [
          {
            "X-API-Key": []
          }
        ],
        "summary": "Delete webhook subscription",
        "tags": [
          "webhooks"
        ]
      },
      "patch": {
        "operationId": "WebhookController_update",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Subscription ID",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateWebhookSubscriptionDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Webhook subscription updated"
          },
          "400": {
            "description": "Invalid event types"
          },
          "404": {
            "description": "Subscription not found"
          }
        },
        "security": [
          {
            "X-API-Key": []
          }
        ],
        "summary": "Update webhook subscription",
        "tags": [
          "webhooks"
        ]
      }
    },
    "/api/v1/webhooks/{id}/complete-rotation": {
      "post": {
        "operationId": "WebhookController_completeRotation",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Subscription ID",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Secret rotation completed"
          },
          "400": {
            "description": "No rotation in progress"
          },
          "404": {
            "description": "Subscription not found"
          }
        },
        "security": [
          {
            "X-API-Key": []
          }
        ],
        "summary": "Complete secret rotation",
        "tags": [
          "webhooks"
        ]
      }
    },
    "/api/v1/webhooks/{id}/delivery-logs": {
      "get": {
        "operationId": "WebhookController_deliveryLogs",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Subscription ID",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Number of logs to return",
            "schema": {
              "default": 20,
              "example": 20,
              "type": "number"
            }
          },
          {
            "name": "offset",
            "required": false,
            "in": "query",
            "description": "Number of logs to skip",
            "schema": {
              "default": 0,
              "example": 0,
              "type": "number"
            }
          },
          {
            "name": "successOnly",
            "required": false,
            "in": "query",
            "description": "Filter for successful deliveries only",
            "schema": {
              "example": false,
              "type": "boolean"
            }
          },
          {
            "name": "failuresOnly",
            "required": false,
            "in": "query",
            "description": "Filter for failed deliveries only",
            "schema": {
              "example": false,
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Delivery logs with pagination"
          },
          "404": {
            "description": "Subscription not found"
          }
        },
        "security": [
          {
            "X-API-Key": []
          }
        ],
        "summary": "Get webhook delivery logs",
        "tags": [
          "webhooks"
        ]
      }
    },
    "/api/v1/webhooks/{id}/reset-circuit-breaker": {
      "post": {
        "operationId": "WebhookController_resetCircuitBreaker",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Subscription ID",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Circuit breaker reset"
          },
          "404": {
            "description": "Subscription not found"
          }
        },
        "security": [
          {
            "X-API-Key": []
          }
        ],
        "summary": "Reset circuit breaker",
        "tags": [
          "webhooks"
        ]
      }
    },
    "/api/v1/webhooks/{id}/rotate-secret": {
      "post": {
        "operationId": "WebhookController_rotateSecret",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Subscription ID",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Secret rotation initiated"
          },
          "404": {
            "description": "Subscription not found"
          }
        },
        "security": [
          {
            "X-API-Key": []
          }
        ],
        "summary": "Rotate webhook secret",
        "tags": [
          "webhooks"
        ]
      }
    },
    "/api/v1/webhooks/{id}/test": {
      "post": {
        "operationId": "WebhookController_test",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Subscription ID",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Test result"
          },
          "404": {
            "description": "Subscription not found"
          }
        },
        "security": [
          {
            "X-API-Key": []
          }
        ],
        "summary": "Test webhook endpoint",
        "tags": [
          "webhooks"
        ]
      }
    }
  },
  "info": {
    "title": "Coinbax API",
    "description": "Blockchain-based payment platform with AI-powered fraud protection and automated dispute resolution",
    "version": "1.0",
    "contact": {}
  },
  "tags": [
    {
      "name": "Authentication",
      "description": "API key management and verification"
    },
    {
      "name": "Controls",
      "description": ""
    },
    {
      "name": "Health",
      "description": "Health check and monitoring endpoints"
    },
    {
      "name": "JWKS",
      "description": ""
    },
    {
      "name": "OAuth 2.0",
      "description": ""
    },
    {
      "name": "Platform",
      "description": ""
    },
    {
      "name": "Transaction Stream",
      "description": ""
    },
    {
      "name": "Transactions",
      "description": "Payment operations and lifecycle management"
    },
    {
      "name": "Verification",
      "description": ""
    },
    {
      "name": "compliance",
      "description": ""
    },
    {
      "name": "disputes",
      "description": ""
    },
    {
      "name": "webhooks",
      "description": ""
    }
  ],
  "servers": [
    {
      "url": "https://api.coinbax.com",
      "description": "Production"
    },
    {
      "url": "https://api-staging.coinbax.com",
      "description": "Staging"
    }
  ],
  "components": {
    "securitySchemes": {
      "api-key": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key",
        "description": "API key for authentication"
      }
    },
    "schemas": {
      "ApiKeyResponseDto": {
        "type": "object",
        "properties": {
          "apiKey": {
            "type": "string",
            "description": "The generated API key (only shown once)",
            "example": "cbx_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"
          },
          "platformId": {
            "type": "string",
            "description": "Platform ID"
          },
          "platformName": {
            "type": "string",
            "description": "Platform name"
          },
          "scopes": {
            "type": "array",
            "description": "API scopes granted to this key",
            "example": [
              "read:transactions",
              "write:transactions",
              "read:customers"
            ],
            "items": {
              "type": "string",
              "enum": [
                "read:transactions",
                "write:transactions",
                "cancel:transactions",
                "rescind:transactions",
                "read:customers",
                "write:customers",
                "read:templates",
                "write:templates",
                "read:disputes",
                "write:disputes",
                "submit:evidence",
                "read:webhooks",
                "write:webhooks",
                "read:compliance",
                "write:compliance",
                "read:platform",
                "write:platform",
                "manage:api-keys",
                "admin:platform"
              ]
            }
          },
          "createdAt": {
            "format": "date-time",
            "type": "string",
            "description": "Creation timestamp"
          },
          "warning": {
            "type": "string",
            "description": "Important security warning",
            "example": "Store this API key securely. It will not be shown again."
          }
        },
        "required": [
          "apiKey",
          "platformId",
          "platformName",
          "scopes",
          "createdAt",
          "warning"
        ]
      },
      "AuthorizeReleaseDto": {
        "type": "object",
        "properties": {
          "early": {
            "type": "boolean",
            "description": "Advisory hint that the sender intends an early (pre-window) release. The server authoritatively infers early vs after-delay from the escrow release time and ignores this value for the decision.",
            "example": true
          }
        }
      },
      "BillingConfigDto": {
        "type": "object",
        "properties": {
          "feeType": {
            "type": "string",
            "description": "Type of fee calculation",
            "enum": [
              "percentage",
              "fixed",
              "tiered"
            ],
            "example": "percentage"
          },
          "feePercentage": {
            "type": "number",
            "description": "Fee percentage in basis points (100 = 1%). Required for percentage type.",
            "example": 30,
            "minimum": 0,
            "maximum": 10000
          },
          "fixedFeeAmount": {
            "type": "number",
            "description": "Fixed fee amount in smallest currency unit. Required for fixed type.",
            "example": 1,
            "minimum": 0
          },
          "tieredFees": {
            "description": "Array of tiered fee configurations. Required for tiered type.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TieredFeeDto"
            }
          },
          "feeWalletAddress": {
            "type": "string",
            "description": "Coinbax fee collection wallet address",
            "example": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
            "maxLength": 100
          },
          "currency": {
            "type": "string",
            "description": "Currency for fee collection",
            "example": "USDC",
            "default": "USDC"
          },
          "isActive": {
            "type": "boolean",
            "description": "Whether billing is active for this workspace",
            "example": true
          }
        },
        "required": [
          "feeType",
          "feeWalletAddress",
          "currency",
          "isActive"
        ]
      },
      "CancelBatchDto": {
        "type": "object",
        "properties": {
          "reason": {
            "type": "string",
            "description": "Human-readable reason recorded on the batch metadata. Surfaces in audit logs and the cancel webhook payload. Truncated at 500 chars.",
            "example": "Sender closed the tab before signing batchCreateEscrow",
            "maxLength": 500
          }
        }
      },
      "CancelPendingTransactionDto": {
        "type": "object",
        "properties": {
          "reason": {
            "type": "string",
            "description": "Optional human-readable reason for the cancellation. Surfaced in the transaction.failed webhook and stored on `metadata.cancelReason`.",
            "example": "User dismissed pending tx from SafeSend Active tab",
            "maxLength": 500
          }
        }
      },
      "CheckVerificationDto": {
        "type": "object",
        "properties": {
          "userId": {
            "type": "string",
            "description": "User ID to check verification status",
            "example": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"
          },
          "type": {
            "type": "string",
            "description": "Type of verification to check",
            "enum": [
              "identity",
              "document",
              "address",
              "phone",
              "email"
            ],
            "example": "identity"
          }
        },
        "required": [
          "userId",
          "type"
        ]
      },
      "CompleteTransactionDto": {
        "type": "object",
        "properties": {
          "note": {
            "type": "string",
            "description": "Optional completion note",
            "example": "Service delivered successfully"
          }
        }
      },
      "ComponentHealthDto": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "up",
              "down",
              "degraded"
            ],
            "description": "Component status",
            "example": "up"
          },
          "responseTime": {
            "type": "number",
            "description": "Response time in milliseconds",
            "example": 12
          },
          "details": {
            "type": "object",
            "description": "Component details",
            "example": {
              "version": "14.2",
              "connections": 10
            }
          },
          "error": {
            "type": "string",
            "description": "Error message if component is down",
            "example": "Connection timeout"
          }
        },
        "required": [
          "status",
          "responseTime"
        ]
      },
      "ControlStatusResponseDto": {
        "type": "object",
        "properties": {
          "transactionId": {
            "type": "string",
            "description": "Transaction ID",
            "example": "123e4567-e89b-12d3-a456-426614174000"
          },
          "awaitingVerification": {
            "type": "boolean",
            "description": "Whether transaction is awaiting verification",
            "example": false
          },
          "controls": {
            "type": "array",
            "description": "Array of control execution results",
            "items": {
              "type": "object",
              "properties": {
                "controlId": {
                  "type": "string",
                  "example": "control-uuid"
                },
                "controlType": {
                  "type": "string",
                  "example": "TimeDelay"
                },
                "phase": {
                  "type": "string",
                  "example": "PRE_ESCROW"
                },
                "success": {
                  "type": "boolean",
                  "example": true
                },
                "isRequired": {
                  "type": "boolean",
                  "example": true
                },
                "durationMs": {
                  "type": "number",
                  "example": 125
                },
                "data": {
                  "type": "object"
                },
                "executedAt": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            }
          },
          "success": {
            "type": "boolean",
            "description": "Overall success status",
            "example": true
          },
          "nextAction": {
            "type": "string",
            "description": "Next action required (if any)",
            "example": "verify_2fa"
          },
          "message": {
            "type": "string",
            "description": "Human-readable status message",
            "example": "All controls executed successfully"
          }
        },
        "required": [
          "transactionId",
          "awaitingVerification",
          "controls",
          "success",
          "message"
        ]
      },
      "CreateApiKeyDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Platform name (3-100 characters)",
            "example": "My Marketplace",
            "minLength": 3,
            "maxLength": 100
          },
          "webhookUrl": {
            "type": "string",
            "description": "Webhook URL for receiving event notifications (must be HTTPS in production)",
            "example": "https://myapp.com/webhooks/coinbax",
            "maxLength": 2048
          },
          "settings": {
            "type": "object",
            "description": "Additional platform settings",
            "example": {
              "theme": "dark",
              "notifications": true
            }
          },
          "scopes": {
            "type": "array",
            "description": "API scopes to grant to this key. If not provided, defaults to read-only scopes.",
            "example": [
              "read:transactions",
              "write:transactions",
              "read:customers"
            ],
            "items": {
              "type": "string",
              "enum": [
                "read:transactions",
                "write:transactions",
                "cancel:transactions",
                "rescind:transactions",
                "read:customers",
                "write:customers",
                "read:templates",
                "write:templates",
                "read:disputes",
                "write:disputes",
                "submit:evidence",
                "read:webhooks",
                "write:webhooks",
                "read:compliance",
                "write:compliance",
                "read:platform",
                "write:platform",
                "manage:api-keys",
                "admin:platform"
              ]
            }
          }
        },
        "required": [
          "name"
        ]
      },
      "CreateBatchLegDto": {
        "type": "object",
        "properties": {
          "toAddress": {
            "type": "string",
            "description": "Receiver blockchain address for this leg.",
            "example": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
            "pattern": "^0x[a-fA-F0-9]{40}$"
          },
          "amount": {
            "type": "number",
            "description": "Per-leg amount (minimum 0.01, maximum 1,000,000).",
            "example": 100.5,
            "minimum": 0.01,
            "maximum": 1000000
          },
          "metadata": {
            "type": "object",
            "description": "Per-leg metadata (e.g., recipient name from the address book). Distinct from batch-level metadata.",
            "example": {
              "recipientLabel": "Alice",
              "orderId": "order_789"
            }
          }
        },
        "required": [
          "toAddress",
          "amount"
        ]
      },
      "CreateDisputeDto": {
        "type": "object",
        "properties": {
          "transactionId": {
            "type": "string",
            "description": "Transaction ID to dispute",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          "initiatedBy": {
            "type": "string",
            "description": "Who is initiating the dispute",
            "enum": [
              "sender",
              "receiver"
            ],
            "example": "sender"
          },
          "reason": {
            "type": "string",
            "description": "Reason for the dispute",
            "example": "Product not as described"
          },
          "metadata": {
            "type": "object",
            "description": "Additional metadata",
            "example": {
              "category": "product_quality"
            }
          }
        },
        "required": [
          "transactionId",
          "initiatedBy",
          "reason"
        ]
      },
      "CreateOAuthClientDto": {
        "type": "object",
        "properties": {
          "platformId": {
            "type": "string",
            "description": "Platform ID the OAuth client belongs to",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          "workspaceId": {
            "type": "string",
            "description": "Optional workspace ID for workspace-scoped clients",
            "example": "550e8400-e29b-41d4-a716-446655440001"
          },
          "name": {
            "type": "string",
            "description": "Name of the OAuth client",
            "example": "My Integration App"
          },
          "description": {
            "type": "string",
            "description": "Description of the OAuth client",
            "example": "Third-party integration for transaction processing"
          },
          "scopes": {
            "description": "List of scopes the client is authorized for",
            "example": [
              "read:transactions",
              "write:transactions"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "grantTypes": {
            "description": "Grant types allowed for this client",
            "example": [
              "client_credentials"
            ],
            "default": [
              "client_credentials"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "redirectUris": {
            "description": "Redirect URIs for authorization code flow (future use)",
            "example": [
              "https://example.com/callback"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "platformId",
          "name",
          "scopes"
        ]
      },
      "CreateOAuthClientResponseDto": {
        "type": "object",
        "properties": {
          "clientId": {
            "type": "string",
            "description": "Generated client ID",
            "example": "coinbax_client_a1b2c3d4e5f6g7h8i9j0"
          },
          "clientSecret": {
            "type": "string",
            "description": "Generated client secret (only shown once)",
            "example": "coinbax_secret_x1y2z3a4b5c6d7e8f9g0h1i2j3k4l5m6n7o8p9q0"
          },
          "name": {
            "type": "string",
            "description": "Name of the OAuth client",
            "example": "My Integration App"
          },
          "scopes": {
            "description": "Scopes granted to this client",
            "example": [
              "read:transactions",
              "write:transactions"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "createdAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the client was created",
            "example": "2026-02-24T12:00:00Z"
          },
          "warning": {
            "type": "string",
            "description": "Warning about client secret storage",
            "example": "Store the client secret securely. It will not be shown again."
          }
        },
        "required": [
          "clientId",
          "clientSecret",
          "name",
          "scopes",
          "createdAt",
          "warning"
        ]
      },
      "CreateQuoteRequestDto": {
        "type": "object",
        "properties": {
          "amountUsd": {
            "type": "string",
            "example": "1000.00",
            "description": "USD principal amount"
          },
          "asset": {
            "type": "string",
            "example": "USDC",
            "maxLength": 16
          },
          "network": {
            "type": "string",
            "example": "base",
            "description": "Canonical network identifier"
          }
        },
        "required": [
          "amountUsd",
          "asset",
          "network"
        ]
      },
      "CreateTransactionBatchDto": {
        "type": "object",
        "properties": {
          "fromAddress": {
            "type": "string",
            "description": "Sender blockchain address (one signer for the whole batch).",
            "example": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
            "pattern": "^0x[a-fA-F0-9]{40}$"
          },
          "legs": {
            "description": "Per-recipient legs (1..MAX_BATCH_LEGS).",
            "minItems": 1,
            "maxItems": 50,
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CreateBatchLegDto"
            }
          },
          "currency": {
            "type": "string",
            "description": "Currency type — shared by every leg in the batch.",
            "enum": [
              "USDC",
              "USDT",
              "ETH",
              "MockUSDC",
              "USDG"
            ],
            "default": "USDC"
          },
          "blockchainNetwork": {
            "type": "string",
            "description": "Blockchain network. The set of accepted values mirrors `CreateTransactionDto.blockchainNetwork`, including the env-aware default ('base' on production, 'base-sepolia' on local/staging), non-prod coercion of mainnet names, and production rejection of testnet names.",
            "example": "base"
          },
          "orchestrationType": {
            "type": "string",
            "description": "Orchestration type for every leg.",
            "enum": [
              "raw",
              "coinbax"
            ],
            "default": "raw"
          },
          "signerType": {
            "type": "string",
            "description": "Signer type. Must be `external_wallet` in B1 — custodial batch sends are not supported in this phase.",
            "enum": [
              "custodial",
              "external_wallet"
            ],
            "default": "external_wallet"
          },
          "templateId": {
            "type": "string",
            "description": "Template ID from coinbax-library. Stored on the batch in B1; control execution against the template is wired in B2.",
            "example": "550e8400-e29b-41d4-a716-446655440000",
            "format": "uuid"
          },
          "customerId": {
            "type": "string",
            "description": "Customer ID for external-wallet flows (Transmitter, SafeSend).",
            "example": "cust_abc123"
          },
          "idempotencyKey": {
            "type": "string",
            "description": "Idempotency key — if provided, an existing batch with the same key is returned instead of creating a new one.",
            "example": "batch_unique_key_12345",
            "maxLength": 64
          },
          "metadata": {
            "type": "object",
            "description": "Batch-level metadata (distinct from per-leg metadata).",
            "example": {
              "source": "safesend",
              "uiVersion": "1.2.3"
            }
          },
          "template": {
            "description": "Embedded template (alternative to `templateId`, deprecated for consistency with `CreateTransactionDto`).",
            "deprecated": true,
            "allOf": [
              {
                "$ref": "#/components/schemas/TemplateDto"
              }
            ]
          }
        },
        "required": [
          "fromAddress",
          "legs"
        ]
      },
      "CreateTransactionDto": {
        "type": "object",
        "properties": {
          "fromAddress": {
            "type": "string",
            "description": "Sender blockchain address",
            "example": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
            "pattern": "^0x[a-fA-F0-9]{40}$"
          },
          "toAddress": {
            "type": "string",
            "description": "Receiver blockchain address",
            "example": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
            "pattern": "^0x[a-fA-F0-9]{40}$"
          },
          "amount": {
            "type": "number",
            "description": "Transaction amount (minimum 0.01, maximum 1,000,000)",
            "example": 100.5,
            "minimum": 0.01,
            "maximum": 1000000
          },
          "currency": {
            "type": "string",
            "description": "Currency type",
            "enum": [
              "USDC",
              "USDT",
              "ETH",
              "MockUSDC",
              "USDG"
            ],
            "default": "USDC"
          },
          "blockchainNetwork": {
            "type": "string",
            "description": "Blockchain network. Defaults to the environment's Base network when omitted ('base' on production, 'base-sepolia' on local/staging). Mainnet names sent to local/staging are coerced to their testnet counterpart (e.g. base → base-sepolia); testnet names sent to production are rejected with TESTNET_NOT_ALLOWED_IN_PRODUCTION.",
            "example": "base",
            "enum": [
              "ethereum",
              "base",
              "solana",
              "optimism",
              "arbitrum",
              "base-sepolia",
              "ethereum-sepolia",
              "optimism-sepolia",
              "arbitrum-sepolia",
              "solana-devnet",
              "solana-testnet"
            ]
          },
          "orchestrationType": {
            "type": "string",
            "description": "Orchestration type: raw (direct blockchain) or coinbax (smart contract escrow)",
            "enum": [
              "raw",
              "coinbax"
            ],
            "default": "raw",
            "example": "raw"
          },
          "templateId": {
            "type": "string",
            "description": "Template ID from coinbax-library. The template will be fetched from the library service.",
            "example": "550e8400-e29b-41d4-a716-446655440000",
            "format": "uuid"
          },
          "contractTemplateId": {
            "type": "string",
            "description": "Contract template ID (alias for templateId, for backward compatibility)",
            "example": "550e8400-e29b-41d4-a716-446655440000",
            "format": "uuid",
            "deprecated": true
          },
          "template": {
            "description": "Complete template data embedded in request (optional, prefer using templateId instead)",
            "deprecated": true,
            "example": {
              "templateId": "550e8400-e29b-41d4-a716-446655440000",
              "templateName": "2FA Escrow",
              "templateVersion": "1.0.0",
              "controls": [
                {
                  "id": "control-123",
                  "type": "TwoFactorAuth",
                  "name": "Two-Factor Authentication",
                  "executionPhase": "PRE_ESCROW",
                  "executionOrder": 1,
                  "config": {
                    "method": "sms",
                    "phoneNumber": "+1234567890"
                  }
                }
              ]
            },
            "allOf": [
              {
                "$ref": "#/components/schemas/TemplateDto"
              }
            ]
          },
          "metadata": {
            "type": "object",
            "description": "Additional metadata about the transaction",
            "example": {
              "orderId": "order_789",
              "productName": "Premium Widget"
            }
          },
          "billingConfig": {
            "description": "Billing configuration from workspace (passed by coinbax-core)",
            "example": {
              "feeType": "percentage",
              "feePercentage": 30,
              "feeWalletAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
              "currency": "USDC",
              "isActive": true
            },
            "allOf": [
              {
                "$ref": "#/components/schemas/BillingConfigDto"
              }
            ]
          },
          "quoteId": {
            "type": "string",
            "description": "FeeQuote id from a prior /transactions/quote call. Required when the workspace has BILLING_ENABLED=true (Phase 1 cutover).",
            "format": "uuid",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          "signerType": {
            "type": "string",
            "description": "Signer type: custodial (Coinbax signs) or external_wallet (User signs via MetaMask)",
            "enum": [
              "custodial",
              "external_wallet"
            ],
            "default": "custodial",
            "example": "custodial"
          },
          "customerId": {
            "type": "string",
            "description": "Customer ID for external wallet transactions (Transmitter users)",
            "example": "cust_abc123"
          },
          "idempotencyKey": {
            "type": "string",
            "description": "Idempotency key to prevent duplicate transaction creation. If provided, a transaction with the same key will return the existing transaction instead of creating a new one.",
            "example": "tx_unique_key_12345",
            "maxLength": 64
          }
        },
        "required": [
          "fromAddress",
          "toAddress",
          "amount"
        ]
      },
      "CreateVerificationDto": {
        "type": "object",
        "properties": {
          "userId": {
            "type": "string",
            "description": "User ID to verify",
            "example": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"
          },
          "type": {
            "type": "string",
            "description": "Type of verification to perform",
            "enum": [
              "identity",
              "document",
              "address",
              "phone",
              "email"
            ],
            "example": "identity"
          },
          "metadata": {
            "type": "object",
            "description": "Additional metadata for verification",
            "example": {
              "email": "user@example.com",
              "country": "US"
            }
          }
        },
        "required": [
          "userId",
          "type"
        ]
      },
      "CreateWebhookSubscriptionDto": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "description": "The URL to send webhook events to",
            "example": "https://api.example.com/webhooks"
          },
          "eventTypes": {
            "description": "Event types to subscribe to",
            "example": [
              "transaction.created",
              "transaction.completed"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "ipWhitelist": {
            "description": "Optional list of IP addresses allowed to send webhooks",
            "example": [
              "1.2.3.4",
              "5.6.7.8"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "metadata": {
            "type": "object",
            "description": "Optional metadata for the subscription",
            "example": {
              "environment": "production",
              "team": "payments"
            }
          }
        },
        "required": [
          "url",
          "eventTypes"
        ]
      },
      "DisputeResponseDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          "transactionId": {
            "type": "string",
            "example": "550e8400-e29b-41d4-a716-446655440001"
          },
          "initiatedBy": {
            "type": "string",
            "enum": [
              "sender",
              "receiver"
            ],
            "example": "sender"
          },
          "currentTier": {
            "type": "string",
            "enum": [
              "tier_1_smart_contract",
              "tier_2_peer_review",
              "tier_3_arbitration"
            ],
            "example": "tier_1_smart_contract"
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "under_review",
              "pending_votes",
              "in_arbitration",
              "resolved"
            ],
            "example": "open"
          },
          "reason": {
            "type": "string",
            "example": "Product not as described"
          },
          "tier1CompletedAt": {
            "type": "object",
            "example": "2024-01-15T10:30:00Z"
          },
          "tier2CompletedAt": {
            "type": "object",
            "example": "2024-01-16T10:30:00Z"
          },
          "resolvedAt": {
            "type": "object",
            "example": "2024-01-17T10:30:00Z"
          },
          "outcome": {
            "type": "string",
            "enum": [
              "favor_sender",
              "favor_receiver",
              "partial_refund"
            ],
            "example": "favor_sender"
          },
          "resolutionReason": {
            "type": "object",
            "example": "Evidence clearly shows product defect"
          },
          "refundPercentage": {
            "type": "object",
            "example": 50
          },
          "metadata": {
            "type": "object"
          },
          "createdAt": {
            "format": "date-time",
            "type": "string",
            "example": "2024-01-15T10:00:00Z"
          },
          "updatedAt": {
            "format": "date-time",
            "type": "string",
            "example": "2024-01-15T10:30:00Z"
          },
          "evidence": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EvidenceResponseDto"
            }
          }
        },
        "required": [
          "id",
          "transactionId",
          "initiatedBy",
          "currentTier",
          "status",
          "reason",
          "createdAt",
          "updatedAt"
        ]
      },
      "EvidenceResponseDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "disputeId": {
            "type": "string"
          },
          "submittedBy": {
            "type": "string"
          },
          "type": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "description": {
            "type": "object"
          },
          "content": {
            "type": "object"
          },
          "fileUrl": {
            "type": "object"
          },
          "fileHash": {
            "type": "object"
          },
          "isEncrypted": {
            "type": "boolean"
          },
          "metadata": {
            "type": "object"
          },
          "createdAt": {
            "format": "date-time",
            "type": "string"
          }
        },
        "required": [
          "id",
          "disputeId",
          "submittedBy",
          "type",
          "title",
          "isEncrypted",
          "createdAt"
        ]
      },
      "FeePreviewRequestDto": {
        "type": "object",
        "properties": {
          "amount": {
            "type": "number",
            "description": "The proposed transaction amount",
            "example": 100.5,
            "minimum": 0.01,
            "maximum": 1000000
          },
          "billingConfig": {
            "description": "Billing configuration from workspace",
            "allOf": [
              {
                "$ref": "#/components/schemas/BillingConfigDto"
              }
            ]
          }
        },
        "required": [
          "amount",
          "billingConfig"
        ]
      },
      "FeePreviewResponseDto": {
        "type": "object",
        "properties": {
          "originalAmount": {
            "type": "number",
            "description": "The original transaction amount",
            "example": 100.5
          },
          "feeAmount": {
            "type": "number",
            "description": "The calculated platform fee",
            "example": 0.3015
          },
          "netAmount": {
            "type": "number",
            "description": "The net amount after fee deduction",
            "example": 100.1985
          },
          "feeType": {
            "type": "string",
            "description": "The type of fee calculation used",
            "example": "percentage",
            "enum": [
              "percentage",
              "fixed",
              "tiered"
            ]
          },
          "feeBasisPoints": {
            "type": "object",
            "description": "The fee rate in basis points (if applicable)",
            "example": 30,
            "nullable": true
          },
          "feePercentageDisplay": {
            "type": "object",
            "description": "The fee percentage displayed for user",
            "example": "0.30%",
            "nullable": true
          },
          "feeWalletAddress": {
            "type": "string",
            "description": "The wallet address for fee collection",
            "example": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"
          },
          "billingActive": {
            "type": "boolean",
            "description": "Whether billing is currently active",
            "example": true
          }
        },
        "required": [
          "originalAmount",
          "feeAmount",
          "netAmount",
          "feeType",
          "feeBasisPoints",
          "feePercentageDisplay",
          "feeWalletAddress",
          "billingActive"
        ]
      },
      "HealthCheckDto": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "healthy",
              "unhealthy",
              "degraded"
            ],
            "description": "Overall system health status",
            "example": "healthy"
          },
          "timestamp": {
            "type": "string",
            "description": "Timestamp of health check",
            "example": "2024-10-14T20:30:00.000Z"
          },
          "version": {
            "type": "string",
            "description": "Application version",
            "example": "0.1.0"
          },
          "uptime": {
            "type": "number",
            "description": "Application uptime in seconds",
            "example": 3600
          },
          "database": {
            "description": "Database health",
            "allOf": [
              {
                "$ref": "#/components/schemas/ComponentHealthDto"
              }
            ]
          },
          "cache": {
            "description": "Redis cache health",
            "allOf": [
              {
                "$ref": "#/components/schemas/ComponentHealthDto"
              }
            ]
          },
          "blockchain": {
            "description": "Blockchain provider health",
            "allOf": [
              {
                "$ref": "#/components/schemas/ComponentHealthDto"
              }
            ]
          },
          "riskService": {
            "description": "Risk service health",
            "allOf": [
              {
                "$ref": "#/components/schemas/ComponentHealthDto"
              }
            ]
          },
          "memory": {
            "type": "object",
            "description": "System memory usage",
            "example": {
              "total": 16777216000,
              "free": 8388608000,
              "used": 8388608000,
              "usedPercent": 50
            }
          },
          "process": {
            "type": "object",
            "description": "Process information",
            "example": {
              "pid": 12345,
              "memoryUsage": 150000000,
              "cpuUsage": 5.5
            }
          }
        },
        "required": [
          "status",
          "timestamp",
          "version",
          "uptime",
          "database",
          "cache",
          "blockchain",
          "riskService",
          "memory",
          "process"
        ]
      },
      "JwkDto": {
        "type": "object",
        "properties": {
          "kty": {
            "type": "string",
            "description": "Key type - always \"RSA\" for RSA keys",
            "example": "RSA"
          },
          "use": {
            "type": "string",
            "description": "Public key use - \"sig\" for signature verification",
            "example": "sig"
          },
          "alg": {
            "type": "string",
            "description": "Algorithm - RS256 (RSA PKCS#1 v1.5 with SHA-256)",
            "example": "RS256"
          },
          "kid": {
            "type": "string",
            "description": "Key ID - unique identifier for this key",
            "example": "coinbax-jh-key-001"
          },
          "n": {
            "type": "string",
            "description": "RSA modulus (Base64URL encoded)",
            "example": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw"
          },
          "e": {
            "type": "string",
            "description": "RSA exponent (Base64URL encoded)",
            "example": "AQAB"
          }
        },
        "required": [
          "kty",
          "use",
          "alg",
          "kid",
          "n",
          "e"
        ]
      },
      "JwksResponseDto": {
        "type": "object",
        "properties": {
          "keys": {
            "description": "Array of JSON Web Keys",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/JwkDto"
            }
          }
        },
        "required": [
          "keys"
        ]
      },
      "LivenessCheckDto": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "description": "Liveness status",
            "example": "alive"
          },
          "timestamp": {
            "type": "string",
            "description": "Timestamp",
            "example": "2024-10-14T20:30:00.000Z"
          },
          "uptime": {
            "type": "number",
            "description": "Application uptime in seconds",
            "example": 3600
          }
        },
        "required": [
          "status",
          "timestamp",
          "uptime"
        ]
      },
      "OAuthClientInfoDto": {
        "type": "object",
        "properties": {
          "clientId": {
            "type": "string",
            "description": "Client ID",
            "example": "coinbax_client_a1b2c3d4e5f6g7h8i9j0"
          },
          "name": {
            "type": "string",
            "description": "Client name",
            "example": "My Integration App"
          },
          "description": {
            "type": "string",
            "description": "Client description",
            "example": "Third-party integration for transaction processing"
          },
          "scopes": {
            "description": "Scopes granted to this client",
            "example": [
              "read:transactions",
              "write:transactions"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "isActive": {
            "type": "boolean",
            "description": "Whether the client is active",
            "example": true
          },
          "createdAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the client was created",
            "example": "2026-02-24T12:00:00Z"
          },
          "lastUsedAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the client was last used",
            "example": "2026-02-24T14:30:00Z"
          }
        },
        "required": [
          "clientId",
          "name",
          "scopes",
          "isActive",
          "createdAt"
        ]
      },
      "OAuthScope": {
        "type": "object",
        "properties": {}
      },
      "OAuthTokenRequestDto": {
        "type": "object",
        "properties": {
          "grant_type": {
            "type": "string",
            "description": "OAuth 2.0 grant type",
            "example": "client_credentials",
            "enum": [
              "client_credentials"
            ]
          },
          "client_id": {
            "type": "string",
            "description": "OAuth client ID",
            "example": "coinbax_client_a1b2c3d4e5f6g7h8i9j0"
          },
          "client_secret": {
            "type": "string",
            "description": "OAuth client secret",
            "example": "coinbax_secret_x1y2z3a4b5c6d7e8f9g0h1i2j3k4l5m6n7o8p9q0"
          },
          "scope": {
            "type": "string",
            "description": "Space-separated list of requested scopes (optional, defaults to all client scopes)",
            "example": "read:transactions write:transactions"
          }
        },
        "required": [
          "grant_type",
          "client_id",
          "client_secret"
        ]
      },
      "OAuthTokenResponseDto": {
        "type": "object",
        "properties": {
          "access_token": {
            "type": "string",
            "description": "JWT access token",
            "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
          },
          "token_type": {
            "type": "string",
            "description": "Token type (always Bearer)",
            "example": "Bearer"
          },
          "expires_in": {
            "type": "number",
            "description": "Token expiration time in seconds",
            "example": 3600
          },
          "scope": {
            "type": "string",
            "description": "Space-separated list of granted scopes",
            "example": "read:transactions write:transactions"
          }
        },
        "required": [
          "access_token",
          "token_type",
          "expires_in",
          "scope"
        ]
      },
      "Platform": {
        "type": "object",
        "properties": {}
      },
      "QuoteResponseDto": {
        "type": "object",
        "properties": {
          "quoteId": {
            "type": "string",
            "format": "uuid"
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time"
          },
          "amountUsd": {
            "type": "string",
            "example": "1000.00"
          },
          "platformFeeUsd": {
            "type": "string",
            "example": "2.500000"
          },
          "workspaceFeeUsd": {
            "type": "object",
            "example": "5.000000",
            "nullable": true
          },
          "gasPassThroughUsd": {
            "type": "object",
            "example": null,
            "nullable": true
          },
          "totalOnTopUsd": {
            "type": "string",
            "example": "7.500000"
          },
          "totalChargedUsd": {
            "type": "string",
            "example": "1007.500000"
          },
          "gasCapExceeded": {
            "type": "boolean",
            "example": false
          }
        },
        "required": [
          "quoteId",
          "expiresAt",
          "amountUsd",
          "platformFeeUsd",
          "workspaceFeeUsd",
          "gasPassThroughUsd",
          "totalOnTopUsd",
          "totalChargedUsd",
          "gasCapExceeded"
        ]
      },
      "ReadinessCheckDto": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "description": "Readiness status",
            "example": "ready"
          },
          "timestamp": {
            "type": "string",
            "description": "Timestamp",
            "example": "2024-10-14T20:30:00.000Z"
          },
          "dependencies": {
            "type": "object",
            "description": "Critical dependencies status",
            "example": {
              "database": "up",
              "cache": "up"
            }
          }
        },
        "required": [
          "status",
          "timestamp",
          "dependencies"
        ]
      },
      "RefundTransactionDto": {
        "type": "object",
        "properties": {
          "reason": {
            "type": "string",
            "description": "Reason for refund",
            "example": "Customer request - item not as described"
          }
        },
        "required": [
          "reason"
        ]
      },
      "RegenerateApiKeyDto": {
        "type": "object",
        "properties": {
          "platformId": {
            "type": "string",
            "description": "Platform ID",
            "example": "123e4567-e89b-12d3-a456-426614174000"
          }
        },
        "required": [
          "platformId"
        ]
      },
      "RescindTransactionDto": {
        "type": "object",
        "properties": {
          "reason": {
            "type": "string",
            "description": "Reason for rescinding the transaction (required if control config has rescindRequiresReason: true)",
            "example": "Changed my mind about the purchase"
          },
          "senderWallet": {
            "type": "string",
            "description": "Sender wallet address for authorization verification",
            "example": "0x1234567890abcdef1234567890abcdef12345678"
          }
        },
        "required": [
          "senderWallet"
        ]
      },
      "ResolveDisputeDto": {
        "type": "object",
        "properties": {
          "outcome": {
            "type": "string",
            "description": "Outcome of the dispute",
            "enum": [
              "favor_sender",
              "favor_receiver",
              "partial_refund"
            ],
            "example": "favor_sender"
          },
          "resolutionReason": {
            "type": "string",
            "description": "Reason for the resolution",
            "example": "Evidence clearly shows product defect"
          },
          "refundPercentage": {
            "type": "number",
            "description": "Refund percentage (0-100) if partial refund",
            "example": 50
          }
        },
        "required": [
          "outcome",
          "resolutionReason"
        ]
      },
      "RevokeTokenDto": {
        "type": "object",
        "properties": {
          "token_jti": {
            "type": "string",
            "description": "JWT ID (jti) of the token to revoke",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          }
        },
        "required": [
          "token_jti"
        ]
      },
      "SubmitBatchEscrowHashDto": {
        "type": "object",
        "properties": {
          "transactionHash": {
            "type": "string",
            "description": "On-chain transaction hash for the `batchCreateEscrow` call. Shared by every leg in the batch (one signature, one tx).",
            "example": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
            "pattern": "^0x[a-fA-F0-9]{64}$"
          }
        },
        "required": [
          "transactionHash"
        ]
      },
      "SubmitEscrowHashDto": {
        "type": "object",
        "properties": {
          "transactionHash": {
            "type": "string",
            "description": "Blockchain transaction hash from user wallet escrow deposit",
            "example": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
          },
          "escrowId": {
            "type": "string",
            "description": "Escrow ID returned by the smart contract (optional - will be detected from blockchain events if not provided)",
            "example": "0x9876543210abcdef9876543210abcdef9876543210abcdef9876543210abcdef"
          }
        },
        "required": [
          "transactionHash"
        ]
      },
      "SubmitEvidenceDto": {
        "type": "object",
        "properties": {
          "submittedBy": {
            "type": "string",
            "description": "Who is submitting the evidence",
            "enum": [
              "sender",
              "receiver"
            ],
            "example": "sender"
          },
          "type": {
            "type": "string",
            "description": "Type of evidence",
            "enum": [
              "text",
              "image",
              "document",
              "screenshot",
              "communication_log",
              "other"
            ],
            "example": "screenshot"
          },
          "title": {
            "type": "string",
            "description": "Title of the evidence",
            "example": "Screenshot of product defect"
          },
          "description": {
            "type": "string",
            "description": "Description of the evidence",
            "example": "This shows the damaged packaging upon delivery"
          },
          "content": {
            "type": "string",
            "description": "Text content (for text evidence)",
            "example": "Conversation log..."
          },
          "fileUrl": {
            "type": "string",
            "description": "File URL (IPFS or storage)",
            "example": "ipfs://QmXyz123..."
          },
          "fileHash": {
            "type": "string",
            "description": "Hash of the file for verification",
            "example": "0xabc123..."
          },
          "isEncrypted": {
            "type": "boolean",
            "description": "Whether the evidence is encrypted",
            "example": true
          },
          "metadata": {
            "type": "object",
            "description": "Additional metadata",
            "example": {
              "fileSize": 1024000,
              "mimeType": "image/png"
            }
          }
        },
        "required": [
          "submittedBy",
          "type",
          "title"
        ]
      },
      "SubmitTransactionHashDto": {
        "type": "object",
        "properties": {
          "transactionHash": {
            "type": "string",
            "description": "Blockchain transaction hash from user wallet",
            "example": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
          }
        },
        "required": [
          "transactionHash"
        ]
      },
      "TemplateConfigResponseDto": {
        "type": "object",
        "properties": {
          "templateId": {
            "type": "string",
            "description": "Template UUID.",
            "example": "a1b2c3d4-e5f6-4a5b-8c9d-1234567890ab"
          },
          "templateName": {
            "type": "string",
            "description": "Template name (e.g. \"Transmitter P2P\").",
            "example": "Transmitter P2P"
          },
          "templateVersion": {
            "type": "string",
            "description": "Template version string from the library (e.g. \"1.0.0\").",
            "example": "1.0.0"
          },
          "controls": {
            "description": "Active controls on the template, sanitized to a small set of public-safe fields per type. See TemplateControlSummary.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TemplateControlSummary"
            }
          }
        },
        "required": [
          "templateId",
          "templateName",
          "templateVersion",
          "controls"
        ]
      },
      "TemplateControlDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Control ID",
            "example": "control-123"
          },
          "type": {
            "type": "string",
            "description": "Control type (e.g., TimeDelay, TwoFactorAuth, ComplianceCheck)",
            "example": "TimeDelay"
          },
          "name": {
            "type": "string",
            "description": "Control name",
            "example": "Payment Hold Period"
          },
          "executionPhase": {
            "type": "string",
            "description": "Execution phase (PRE_ESCROW, POST_ESCROW, POST_RELEASE)",
            "example": "PRE_ESCROW"
          },
          "executionOrder": {
            "type": "number",
            "description": "Execution order within the phase",
            "example": 1
          },
          "config": {
            "type": "object",
            "description": "Control configuration",
            "example": {
              "delayMinutes": 5
            }
          },
          "isRequired": {
            "type": "boolean",
            "description": "Whether this control is required for the transaction",
            "example": true,
            "default": false
          },
          "isActive": {
            "type": "boolean",
            "description": "Whether this control is active",
            "example": true,
            "default": true
          }
        },
        "required": [
          "id",
          "type",
          "name",
          "executionPhase",
          "executionOrder",
          "config"
        ]
      },
      "TemplateControlSummary": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "description": "Control type. Tolerates the upstream `ChainlysisSanctions` typo alongside the corrected `ChainalysisSanctions` for now (see PR #74).",
            "example": "AmountLimit"
          },
          "phase": {
            "type": "string",
            "description": "Execution phase: PRE_ESCROW, POST_ESCROW, PRE_RELEASE, or POST_RELEASE.",
            "example": "PRE_ESCROW"
          },
          "name": {
            "type": "string",
            "description": "Human-readable control name (from the template, e.g. \"Sanctions Screening\").",
            "example": "Amount Limit Check"
          },
          "isRequired": {
            "type": "boolean",
            "description": "Whether this control is required (vs. optional — driven by `metadata.optional` on the template control).",
            "example": true
          },
          "minAmount": {
            "type": "number",
            "description": "AmountLimit: minimum allowed amount per send (inclusive).",
            "example": 1
          },
          "maxAmount": {
            "type": "number",
            "description": "AmountLimit: maximum allowed amount per send (inclusive).",
            "example": 1000000
          },
          "currency": {
            "type": "string",
            "description": "AmountLimit: currency the limits are denominated in (e.g. \"USD\", \"USDC\").",
            "example": "USD"
          },
          "delayAmount": {
            "type": "number",
            "description": "TimeDelay: hold-period duration amount.",
            "example": 24
          },
          "delayUnit": {
            "type": "string",
            "description": "TimeDelay: unit for `delayAmount`.",
            "example": "hours",
            "enum": [
              "seconds",
              "minutes",
              "hours",
              "days"
            ]
          },
          "allowSenderRescind": {
            "type": "boolean",
            "description": "TimeDelay: whether the sender can rescind/cancel during the hold window.",
            "example": true
          },
          "userConfigurable": {
            "type": "boolean",
            "description": "TimeDelay: whether the user can pick a delay value per transaction (vs. always the template default). When true, the frontend should render `options` as selectable presets. When false or absent, the frontend renders the template default as a read-only display.",
            "example": true
          },
          "options": {
            "description": "TimeDelay: allowed preset values (when `userConfigurable` is true) using compact duration notation: `Nm` minutes, `Nh` hours, `Nd` days. Frontend parses and renders these as picker chips. Empty / absent means no template-defined presets — frontend may fall back to its own defaults.",
            "example": [
              "5m",
              "30m",
              "24h",
              "48h",
              "7d"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sanctionsLists": {
            "description": "ChainlysisSanctions / ChainalysisSanctions: which sanctions lists the screening checks (e.g. OFAC, SDN, EU).",
            "example": [
              "OFAC",
              "SDN",
              "EU"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "checksSender": {
            "type": "boolean",
            "description": "ChainlysisSanctions / ChainalysisSanctions: whether the sender wallet is screened.",
            "example": true
          },
          "checksRecipient": {
            "type": "boolean",
            "description": "ChainlysisSanctions / ChainalysisSanctions: whether the recipient wallet is screened.",
            "example": true
          },
          "verificationRequired": {
            "type": "boolean",
            "description": "TwilioSMS: whether the SMS-verification step is mandatory (vs. lookup-only).",
            "example": true
          }
        },
        "required": [
          "type",
          "phase",
          "name",
          "isRequired"
        ]
      },
      "TemplateDto": {
        "type": "object",
        "properties": {
          "templateId": {
            "type": "string",
            "description": "Template ID from coinbax-library",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          "templateName": {
            "type": "string",
            "description": "Template name",
            "example": "2FA Escrow"
          },
          "templateVersion": {
            "type": "string",
            "description": "Template version",
            "example": "1.0.0"
          },
          "controls": {
            "description": "Array of controls to execute",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TemplateControlDto"
            }
          }
        },
        "required": [
          "templateId",
          "templateName",
          "templateVersion",
          "controls"
        ]
      },
      "TieredFeeDto": {
        "type": "object",
        "properties": {
          "minAmount": {
            "type": "number",
            "description": "Minimum transaction amount for this tier",
            "example": 0,
            "minimum": 0
          },
          "maxAmount": {
            "type": "number",
            "description": "Maximum transaction amount for this tier",
            "example": 1000,
            "minimum": 0
          },
          "feePercentage": {
            "type": "number",
            "description": "Fee percentage in basis points (100 = 1%)",
            "example": 30,
            "minimum": 0,
            "maximum": 10000
          }
        },
        "required": [
          "minAmount",
          "maxAmount",
          "feePercentage"
        ]
      },
      "Transaction": {
        "type": "object",
        "properties": {
          "rescind": {
            "nullable": true,
            "description": "Public-safe summary of the TimeDelay rescind config. Lets clients gate the Cancel/Rescind affordance without a second round-trip. Null when the transaction has no TimeDelay control (RAW orchestration, templates without a hold period). Absent from response on the list endpoint and on legacy api revisions.",
            "allOf": [
              {
                "$ref": "#/components/schemas/TransactionRescindInfo"
              }
            ]
          }
        }
      },
      "TransactionBatch": {
        "type": "object",
        "properties": {}
      },
      "TransactionRescindInfo": {
        "type": "object",
        "properties": {
          "allowed": {
            "type": "boolean",
            "description": "Whether the sender is allowed to rescind/cancel the transaction during the hold window. Defaults to true when not explicitly set on the underlying control config.",
            "example": true
          },
          "deadline": {
            "type": "object",
            "description": "ISO 8601 timestamp after which rescind is no longer allowed. Null when the rescind window matches the full hold period.",
            "example": "2026-06-15T12:00:00.000Z",
            "nullable": true
          },
          "requiresReason": {
            "type": "boolean",
            "description": "Whether the rescind endpoint requires a non-empty `reason` field in the request body.",
            "example": false
          },
          "notifyRecipient": {
            "type": "boolean",
            "description": "Whether the recipient is notified (e.g. via SMS) when a rescind succeeds.",
            "example": true
          }
        },
        "required": [
          "allowed",
          "deadline",
          "requiresReason",
          "notifyRecipient"
        ]
      },
      "UpdateOAuthClientDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Name of the OAuth client",
            "example": "Updated Integration App"
          },
          "description": {
            "type": "string",
            "description": "Description of the OAuth client",
            "example": "Updated description"
          },
          "scopes": {
            "description": "Updated list of scopes",
            "example": [
              "read:transactions",
              "write:transactions",
              "read:customers"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "isActive": {
            "type": "boolean",
            "description": "Whether the client is active",
            "example": true
          }
        }
      },
      "UpdatePlatformDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Platform name"
          },
          "webhookUrl": {
            "type": "string",
            "description": "Webhook URL"
          },
          "isActive": {
            "type": "boolean",
            "description": "Platform active status"
          },
          "settings": {
            "type": "object",
            "description": "Platform settings"
          },
          "scopes": {
            "type": "array",
            "description": "API scopes granted to this platform",
            "example": [
              "read:transactions",
              "write:transactions",
              "read:customers"
            ],
            "items": {
              "type": "string",
              "enum": [
                "read:transactions",
                "write:transactions",
                "cancel:transactions",
                "rescind:transactions",
                "read:customers",
                "write:customers",
                "read:templates",
                "write:templates",
                "read:disputes",
                "write:disputes",
                "submit:evidence",
                "read:webhooks",
                "write:webhooks",
                "read:compliance",
                "write:compliance",
                "read:platform",
                "write:platform",
                "manage:api-keys",
                "admin:platform"
              ]
            }
          }
        }
      },
      "UpdateWebhookSubscriptionDto": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "description": "The URL to send webhook events to",
            "example": "https://api.example.com/webhooks/v2"
          },
          "eventTypes": {
            "description": "Event types to subscribe to",
            "example": [
              "transaction.created",
              "transaction.completed",
              "transaction.refunded"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "isActive": {
            "type": "boolean",
            "description": "Whether the subscription is active",
            "example": true
          },
          "ipWhitelist": {
            "description": "Optional list of IP addresses allowed to send webhooks",
            "example": [
              "1.2.3.4",
              "5.6.7.8"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "metadata": {
            "type": "object",
            "description": "Optional metadata for the subscription",
            "example": {
              "environment": "production",
              "team": "payments"
            }
          }
        }
      },
      "VerificationResponseDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          "platformId": {
            "type": "string",
            "example": "550e8400-e29b-41d4-a716-446655440001"
          },
          "userId": {
            "type": "string",
            "example": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"
          },
          "type": {
            "type": "string",
            "enum": [
              "identity",
              "document",
              "address",
              "phone",
              "email"
            ],
            "example": "identity"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "in_progress",
              "approved",
              "rejected",
              "needs_review",
              "expired"
            ],
            "example": "approved"
          },
          "level": {
            "type": "string",
            "enum": [
              "none",
              "basic",
              "intermediate",
              "advanced"
            ],
            "example": "basic"
          },
          "riskRating": {
            "type": "string",
            "enum": [
              "low",
              "medium",
              "high",
              "very_high"
            ],
            "example": "low"
          },
          "provider": {
            "type": "object",
            "example": "persona"
          },
          "externalInquiryId": {
            "type": "object",
            "example": "inq_ABC123"
          },
          "externalSessionToken": {
            "type": "object",
            "example": "session_token_xyz"
          },
          "metadata": {
            "type": "object",
            "example": {
              "email": "user@example.com",
              "country": "US"
            }
          },
          "rejectionReason": {
            "type": "object",
            "example": null
          },
          "verifiedAt": {
            "type": "object",
            "example": "2024-01-15T10:30:00Z"
          },
          "expiresAt": {
            "type": "object",
            "example": "2025-01-15T10:30:00Z"
          },
          "createdAt": {
            "format": "date-time",
            "type": "string",
            "example": "2024-01-15T10:00:00Z"
          },
          "updatedAt": {
            "format": "date-time",
            "type": "string",
            "example": "2024-01-15T10:30:00Z"
          }
        },
        "required": [
          "id",
          "platformId",
          "userId",
          "type",
          "status",
          "level",
          "createdAt",
          "updatedAt"
        ]
      },
      "Verify2FADto": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "description": "6-digit verification code sent via SMS",
            "example": "123456",
            "minLength": 6,
            "maxLength": 6
          }
        },
        "required": [
          "code"
        ]
      },
      "VerifyApiKeyResponseDto": {
        "type": "object",
        "properties": {
          "valid": {
            "type": "boolean"
          },
          "platformId": {
            "type": "object",
            "nullable": true
          },
          "platformName": {
            "type": "object",
            "nullable": true
          },
          "isActive": {
            "type": "boolean"
          }
        },
        "required": [
          "valid",
          "platformId",
          "platformName",
          "isActive"
        ]
      },
      "VerifyCodeDto": {
        "type": "object",
        "properties": {}
      }
    }
  }
}
