{
  "openapi": "3.1.0",
  "info": {
    "title": "Forja Issuance Platform API",
    "version": "0.5.0",
    "summary": "Non-custodial ERC-20 mint and redeem API for sandbox integration.",
    "description": "Forja never holds private keys and never signs. Integrators authenticate with HMAC, request mint issuances or redemptions, receive unsigned EVM transactions, sign in their own infrastructure, then report a txHash or rawSignedTransaction for Forja to relay and watch."
  },
  "servers": [
    {
      "url": "https://api.sandbox.forja.capital",
      "description": "Forja sandbox"
    },
    {
      "url": "http://localhost:8080",
      "description": "Local development"
    }
  ],
  "tags": [
    { "name": "System" },
    { "name": "Integrator Auth" },
    { "name": "Tokens" },
    { "name": "Issuances" },
    { "name": "Redemptions" },
    { "name": "Chain RPC" },
    { "name": "Console Admin" }
  ],
  "security": [
    { "HmacAuth": [] }
  ],
  "paths": {
    "/v1/health": {
      "get": {
        "tags": ["System"],
        "summary": "Health check",
        "security": [],
        "responses": {
          "200": {
            "description": "API is alive",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/HealthResponse" }
              }
            }
          }
        }
      }
    },
    "/v1/openapi.json": {
      "get": {
        "tags": ["System"],
        "summary": "Fetch this OpenAPI document",
        "security": [],
        "responses": {
          "200": {
            "description": "OpenAPI JSON document"
          }
        }
      }
    },
    "/v1/auth/verify": {
      "get": {
        "tags": ["Integrator Auth"],
        "summary": "Verify HMAC credentials",
        "description": "Use this endpoint before starting integration. A successful response returns the issuer id, environment, and scopes attached to the API key.",
        "responses": {
          "200": {
            "description": "Authenticated identity",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/AuthIdentity" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" }
        }
      }
    },
    "/v1/tokens": {
      "get": {
        "tags": ["Tokens"],
        "summary": "List registered tokens",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": { "type": "string", "enum": ["active", "paused", "disabled"] }
          }
        ],
        "responses": {
          "200": {
            "description": "Token list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["data"],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/Token" }
                    }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" }
        }
      }
    },
    "/v1/tokens/{tokenId}": {
      "get": {
        "tags": ["Tokens"],
        "summary": "Get a registered token",
        "parameters": [
          { "$ref": "#/components/parameters/TokenId" }
        ],
        "responses": {
          "200": {
            "description": "Token",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Token" }
              }
            }
          },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/v1/tokens/{tokenId}/issuances": {
      "post": {
        "tags": ["Issuances"],
        "summary": "Create an unsigned ERC-20 mint issuance",
        "description": "Creates an issuance in `unsigned` state and returns the full transaction to sign. HMAC clients must provide an `Idempotency-Key` header.",
        "parameters": [
          { "$ref": "#/components/parameters/TokenId" },
          { "$ref": "#/components/parameters/IdempotencyKey" }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/CreateIssuanceRequest" },
              "example": {
                "amount": "1000.00",
                "destinationAddress": "0x1111111111111111111111111111111111111111",
                "reference": "sandbox-test-001",
                "metadata": { "purpose": "amoy-poc" }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "New issuance",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Issuance" }
              }
            }
          },
          "200": {
            "description": "Idempotent replay of an existing issuance",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Issuance" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "422": { "$ref": "#/components/responses/Unprocessable" }
        }
      }
    },
    "/v1/issuances": {
      "get": {
        "tags": ["Issuances"],
        "summary": "List issuances",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": { "$ref": "#/components/schemas/OperationStatus" }
          },
          {
            "name": "tokenId",
            "in": "query",
            "schema": { "type": "string" }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": { "type": "integer", "minimum": 1, "maximum": 200, "default": 50 }
          }
        ],
        "responses": {
          "200": {
            "description": "Issuance list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["data"],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/Issuance" }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/issuances/{issuanceId}": {
      "get": {
        "tags": ["Issuances"],
        "summary": "Get issuance status",
        "parameters": [
          { "$ref": "#/components/parameters/IssuanceId" }
        ],
        "responses": {
          "200": {
            "description": "Issuance",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Issuance" }
              }
            }
          },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/v1/issuances/{issuanceId}/signed": {
      "post": {
        "tags": ["Issuances"],
        "summary": "Report signed issuance transaction",
        "description": "Provide exactly one of `txHash` or `rawSignedTransaction`. If `rawSignedTransaction` is provided, Forja broadcasts it through the configured network RPC.",
        "parameters": [
          { "$ref": "#/components/parameters/IssuanceId" }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/ReportSignedIssuanceRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Submitted issuance",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Issuance" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "409": { "$ref": "#/components/responses/Conflict" }
        }
      }
    },
    "/v1/tokens/{tokenId}/redemptions": {
      "post": {
        "tags": ["Redemptions"],
        "summary": "Create a redemption request",
        "description": "Creates a redemption in `requested` state. The burn transaction is built after receipt confirmation. HMAC clients must provide `Idempotency-Key` or use `clientReference` as the idempotency key.",
        "parameters": [
          { "$ref": "#/components/parameters/TokenId" },
          { "$ref": "#/components/parameters/IdempotencyKeyOptional" }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/CreateRedemptionRequest" },
              "example": {
                "amount": "100.00",
                "clientReference": "bloquo-settlement-001",
                "redeemerAddress": "0x1111111111111111111111111111111111111111",
                "metadata": { "asset": "BBRL" }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "New redemption",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Redemption" }
              }
            }
          },
          "200": {
            "description": "Idempotent replay",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Redemption" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "422": { "$ref": "#/components/responses/Unprocessable" }
        }
      }
    },
    "/v1/redemptions": {
      "get": {
        "tags": ["Redemptions"],
        "summary": "List redemptions",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": { "$ref": "#/components/schemas/RedemptionStatus" }
          },
          {
            "name": "tokenId",
            "in": "query",
            "schema": { "type": "string" }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": { "type": "integer", "minimum": 1, "maximum": 200, "default": 50 }
          }
        ],
        "responses": {
          "200": {
            "description": "Redemption list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["data"],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/Redemption" }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/redemptions/{redemptionId}": {
      "get": {
        "tags": ["Redemptions"],
        "summary": "Get redemption status",
        "parameters": [
          { "$ref": "#/components/parameters/RedemptionId" }
        ],
        "responses": {
          "200": {
            "description": "Redemption",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Redemption" }
              }
            }
          },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/v1/redemptions/{redemptionId}/receipt": {
      "post": {
        "tags": ["Redemptions"],
        "summary": "Confirm treasury receipt and build burn transaction",
        "parameters": [
          { "$ref": "#/components/parameters/RedemptionId" }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/ConfirmRedemptionReceiptRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Redemption with unsigned burn transaction",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Redemption" }
              }
            }
          },
          "409": { "$ref": "#/components/responses/Conflict" }
        }
      }
    },
    "/v1/redemptions/{redemptionId}/signed": {
      "post": {
        "tags": ["Redemptions"],
        "summary": "Report signed burn transaction",
        "description": "Provide exactly one of `txHash` or `rawSignedTransaction`.",
        "parameters": [
          { "$ref": "#/components/parameters/RedemptionId" }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/ReportSignedIssuanceRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Submitted redemption",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Redemption" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "409": { "$ref": "#/components/responses/Conflict" }
        }
      }
    },
    "/v1/rpc/{network}": {
      "post": {
        "tags": ["Chain RPC"],
        "summary": "Send an allowlisted JSON-RPC request",
        "description": "Synchronous chain RPC relay for client backends that sign transactions outside Forja. Use it for gas, nonce, call, receipt, and raw transaction broadcast operations. The method allowlist is intentionally narrow.",
        "parameters": [
          {
            "name": "network",
            "in": "path",
            "required": true,
            "schema": { "$ref": "#/components/schemas/Network" }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/JsonRpcRelayRequest" },
              "examples": {
                "balance": {
                  "value": {
                    "jsonrpc": "2.0",
                    "id": "balance-1",
                    "method": "eth_getBalance",
                    "params": ["0x1111111111111111111111111111111111111111", "latest"]
                  }
                },
                "sendRawTransaction": {
                  "value": {
                    "jsonrpc": "2.0",
                    "id": "send-1",
                    "method": "eth_sendRawTransaction",
                    "params": ["0x02..."]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "JSON-RPC response wrapper",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/JsonRpcRelayResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" }
        }
      }
    },
    "/v1/transactions/broadcast": {
      "post": {
        "tags": ["Chain RPC"],
        "summary": "Broadcast a raw signed EVM transaction",
        "description": "Broadcasts a raw transaction signed by the client backend EOA and returns the transaction hash.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/BroadcastTransactionRequest" },
              "example": {
                "network": "polygon-amoy",
                "rawSignedTransaction": "0x02..."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Broadcast transaction hash",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/BroadcastTransactionResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" }
        }
      }
    },
    "/v1/console/api-keys": {
      "get": {
        "tags": ["Console Admin"],
        "summary": "List API keys",
        "security": [{ "BearerSession": [] }],
        "responses": {
          "200": {
            "description": "API keys without secrets",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["data"],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/ApiKeyRecord" }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": ["Console Admin"],
        "summary": "Create API key",
        "description": "Returns the API secret once. Store it immediately.",
        "security": [{ "BearerSession": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/CreateApiKeyRequest" }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created API key and one-time secret",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/CreatedApiKey" }
              }
            }
          }
        }
      }
    },
    "/v1/console/api-keys/{apiKey}": {
      "patch": {
        "tags": ["Console Admin"],
        "summary": "Enable or disable API key",
        "security": [{ "BearerSession": [] }],
        "parameters": [
          {
            "name": "apiKey",
            "in": "path",
            "required": true,
            "schema": { "type": "string" }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["isActive"],
                "properties": {
                  "isActive": { "type": "boolean" }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated API key",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ApiKeyRecord" }
              }
            }
          }
        }
      }
    },
    "/v1/console/team": {
      "get": {
        "tags": ["Console Admin"],
        "summary": "List console users",
        "security": [{ "BearerSession": [] }],
        "responses": {
          "200": {
            "description": "Team users",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["data"],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/ConsoleUser" }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": ["Console Admin"],
        "summary": "Invite console user",
        "security": [{ "BearerSession": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/InviteConsoleUserRequest" }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Invited user",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/InvitedConsoleUserResponse" }
              }
            }
          }
        }
      }
    },
    "/v1/console/team/{userId}": {
      "patch": {
        "tags": ["Console Admin"],
        "summary": "Update console user role/status",
        "security": [{ "BearerSession": [] }],
        "parameters": [
          { "$ref": "#/components/parameters/UserId" }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/UpdateConsoleUserRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated user",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ConsoleUser" }
              }
            }
          }
        }
      }
    },
    "/v1/console/tokens": {
      "post": {
        "tags": ["Console Admin"],
        "summary": "Register token for issuer",
        "security": [{ "BearerSession": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/RegisterTokenRequest" }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Registered token",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Token" }
              }
            }
          }
        }
      }
    },
    "/v1/console/tokens/{tokenId}": {
      "put": {
        "tags": ["Console Admin"],
        "summary": "Update token configuration",
        "security": [{ "BearerSession": [] }],
        "parameters": [
          { "$ref": "#/components/parameters/TokenId" }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/RegisterTokenRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated token",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Token" }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "HmacAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key",
        "description": "HMAC authentication requires x-api-key, x-timestamp, x-nonce, and x-signature. Signing string: METHOD + newline + PATH_WITH_QUERY + newline + RAW_JSON_BODY + newline + UNIX_TIMESTAMP + newline + NONCE. Signature: hex HMAC-SHA256 using the API secret."
      },
      "BearerSession": {
        "type": "http",
        "scheme": "bearer",
        "description": "Console session token returned by the passwordless login flow. The API accepts it as an Authorization: Bearer token; the browser console can also send it in the forja_session cookie."
      }
    },
    "parameters": {
      "TokenId": {
        "name": "tokenId",
        "in": "path",
        "required": true,
        "schema": { "type": "string" }
      },
      "IssuanceId": {
        "name": "issuanceId",
        "in": "path",
        "required": true,
        "schema": { "type": "string", "format": "uuid" }
      },
      "RedemptionId": {
        "name": "redemptionId",
        "in": "path",
        "required": true,
        "schema": { "type": "string", "format": "uuid" }
      },
      "UserId": {
        "name": "userId",
        "in": "path",
        "required": true,
        "schema": { "type": "string", "format": "uuid" }
      },
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": true,
        "schema": { "type": "string" }
      },
      "IdempotencyKeyOptional": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "schema": { "type": "string" },
        "description": "Required for HMAC clients unless clientReference is used as the idempotency key."
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Invalid request",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "Unauthorized": {
        "description": "Missing or invalid authentication",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "Forbidden": {
        "description": "Authenticated but not allowed",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "NotFound": {
        "description": "Resource not found",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "Conflict": {
        "description": "Conflict with current resource state",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "Unprocessable": {
        "description": "Valid request shape, but token or issuance cannot be processed",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      }
    },
    "schemas": {
      "HealthResponse": {
        "type": "object",
        "required": ["status"],
        "properties": {
          "status": { "type": "string", "const": "ok" },
          "version": { "type": "string", "example": "0.2.0" },
          "service": { "type": "string", "example": "forja-api" }
        }
      },
      "AuthIdentity": {
        "type": "object",
        "required": ["clientId", "issuerId", "scopes", "environment"],
        "properties": {
          "clientId": { "type": "string" },
          "issuerId": { "type": "string" },
          "scopes": { "type": "array", "items": { "type": "string" } },
          "environment": { "type": "string", "enum": ["sandbox", "production"] }
        }
      },
      "Token": {
        "type": "object",
        "required": ["id", "issuerId", "symbol", "name", "decimals", "network", "contractAddress", "standard", "status", "governanceMode"],
        "properties": {
          "id": { "type": "string" },
          "issuerId": { "type": "string" },
          "symbol": { "type": "string" },
          "name": { "type": "string" },
          "decimals": { "type": "integer" },
          "network": { "$ref": "#/components/schemas/Network" },
          "contractAddress": { "type": "string", "pattern": "^0x[a-fA-F0-9]{40}$" },
          "implementationAddress": { "type": ["string", "null"] },
          "standard": { "type": "string", "enum": ["ERC20", "ERC20-upgradeable"] },
          "status": { "type": "string", "enum": ["active", "paused", "disabled"] },
          "governanceMode": { "type": "string", "enum": ["eoa", "safe-moduleguard"] },
          "governance": { "$ref": "#/components/schemas/EoaGovernance" },
          "mintAuthority": { "$ref": "#/components/schemas/EoaMintAuthority" },
          "createdAt": { "type": "string", "format": "date-time" },
          "updatedAt": { "type": "string", "format": "date-time" }
        }
      },
      "EoaGovernance": {
        "type": ["object", "null"],
        "properties": {
          "type": { "type": "string", "const": "eoa" },
          "signer": { "type": "string", "pattern": "^0x[a-fA-F0-9]{40}$" }
        }
      },
      "EoaMintAuthority": {
        "type": ["object", "null"],
        "properties": {
          "type": { "type": "string", "const": "eoa" },
          "minter": { "type": "string", "pattern": "^0x[a-fA-F0-9]{40}$" }
        }
      },
      "CreateIssuanceRequest": {
        "type": "object",
        "required": ["amount", "destinationAddress"],
        "properties": {
          "amount": { "type": "string", "pattern": "^\\d+(\\.\\d+)?$" },
          "destinationAddress": { "type": "string", "pattern": "^0x[a-fA-F0-9]{40}$" },
          "reference": { "type": "string" },
          "metadata": {
            "type": "object",
            "additionalProperties": { "type": "string" }
          }
        }
      },
      "Issuance": {
        "type": "object",
        "required": ["id", "tokenId", "issuerId", "amount", "destinationAddress", "status", "unsignedTransaction"],
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "tokenId": { "type": "string" },
          "issuerId": { "type": "string" },
          "amount": { "type": "string" },
          "destinationAddress": { "type": "string" },
          "reference": { "type": ["string", "null"] },
          "metadata": { "type": "object", "additionalProperties": true },
          "status": { "$ref": "#/components/schemas/OperationStatus" },
          "unsignedTransaction": { "$ref": "#/components/schemas/UnsignedTransaction" },
          "txHash": { "type": ["string", "null"] },
          "rawSignedTransaction": { "type": ["string", "null"] },
          "preflight": {
            "type": "object",
            "additionalProperties": true
          },
          "expiresAt": { "type": "string", "format": "date-time" },
          "createdAt": { "type": "string", "format": "date-time" },
          "updatedAt": { "type": "string", "format": "date-time" }
        }
      },
      "UnsignedTransaction": {
        "type": "object",
        "required": ["chainId", "to", "data", "value"],
        "properties": {
          "chainId": { "type": "integer" },
          "from": { "type": "string" },
          "to": { "type": "string" },
          "data": { "type": "string" },
          "value": { "type": "string" },
          "nonce": { "type": ["integer", "null"] },
          "gasLimit": { "type": ["string", "null"] },
          "maxFeePerGas": { "type": ["string", "null"] },
          "maxPriorityFeePerGas": { "type": ["string", "null"] },
          "signingPayload": {
            "type": ["object", "null"],
            "properties": {
              "unsignedSerialized": { "type": "string" },
              "signingHash": { "type": "string" },
              "type": { "type": "string", "const": "eip1559" }
            }
          }
        }
      },
      "ReportSignedIssuanceRequest": {
        "type": "object",
        "description": "Provide exactly one field.",
        "properties": {
          "txHash": { "type": "string" },
          "rawSignedTransaction": { "type": "string" }
        }
      },
      "CreateRedemptionRequest": {
        "type": "object",
        "required": ["amount", "clientReference"],
        "properties": {
          "amount": { "type": "string", "pattern": "^\\d+(\\.\\d+)?$" },
          "clientReference": { "type": "string", "description": "Bloquo settlement id; also used as idempotency key when Idempotency-Key header is omitted." },
          "redeemerAddress": { "type": "string", "pattern": "^0x[a-fA-F0-9]{40}$" },
          "metadata": {
            "type": "object",
            "additionalProperties": { "type": "string" }
          }
        }
      },
      "ConfirmRedemptionReceiptRequest": {
        "type": "object",
        "properties": {
          "receiptTxHash": { "type": "string", "description": "Inbound transfer of returned tokens to treasury." }
        }
      },
      "Redemption": {
        "type": "object",
        "required": ["id", "tokenId", "issuerId", "amount", "clientReference", "status"],
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "tokenId": { "type": "string" },
          "issuerId": { "type": "string" },
          "amount": { "type": "string" },
          "clientReference": { "type": "string" },
          "redeemerAddress": { "type": ["string", "null"] },
          "burnFromAddress": { "type": ["string", "null"] },
          "status": { "$ref": "#/components/schemas/RedemptionStatus" },
          "receiptTxHash": { "type": ["string", "null"] },
          "receivedAt": { "type": ["string", "null"], "format": "date-time" },
          "unsignedTransaction": { "$ref": "#/components/schemas/UnsignedTransaction" },
          "txHash": { "type": ["string", "null"] },
          "rawSignedTransaction": { "type": ["string", "null"] },
          "blockNumber": { "type": ["integer", "null"] },
          "metadata": { "type": "object", "additionalProperties": true },
          "failureReason": { "type": ["string", "null"] },
          "expiresAt": { "type": ["string", "null"], "format": "date-time" },
          "createdAt": { "type": "string", "format": "date-time" },
          "updatedAt": { "type": "string", "format": "date-time" }
        }
      },
      "JsonRpcRelayRequest": {
        "type": "object",
        "required": ["method"],
        "properties": {
          "jsonrpc": { "type": "string", "const": "2.0", "default": "2.0" },
          "id": { "type": ["string", "number", "null"] },
          "method": {
            "type": "string",
            "enum": [
              "eth_blockNumber",
              "eth_call",
              "eth_estimateGas",
              "eth_feeHistory",
              "eth_gasPrice",
              "eth_getBalance",
              "eth_getBlockByNumber",
              "eth_getTransactionByHash",
              "eth_getTransactionReceipt",
              "eth_sendRawTransaction"
            ]
          },
          "params": {
            "type": "array",
            "items": true,
            "default": []
          }
        }
      },
      "JsonRpcRelayResponse": {
        "type": "object",
        "required": ["jsonrpc", "id", "result"],
        "properties": {
          "jsonrpc": { "type": "string", "const": "2.0" },
          "id": { "type": ["string", "number", "null"] },
          "result": true
        }
      },
      "BroadcastTransactionRequest": {
        "type": "object",
        "required": ["network", "rawSignedTransaction"],
        "properties": {
          "network": { "$ref": "#/components/schemas/Network" },
          "rawSignedTransaction": { "type": "string", "pattern": "^0x[a-fA-F0-9]+$" }
        }
      },
      "BroadcastTransactionResponse": {
        "type": "object",
        "required": ["txHash"],
        "properties": {
          "txHash": { "type": "string", "pattern": "^0x[a-fA-F0-9]{64}$" }
        }
      },
      "RegisterTokenRequest": {
        "type": "object",
        "required": ["symbol", "name", "decimals", "network", "contractAddress", "standard", "minter"],
        "properties": {
          "id": { "type": "string" },
          "symbol": { "type": "string" },
          "name": { "type": "string" },
          "decimals": { "type": "integer", "minimum": 0, "maximum": 36 },
          "network": { "$ref": "#/components/schemas/Network" },
          "contractAddress": { "type": "string", "pattern": "^0x[a-fA-F0-9]{40}$" },
          "implementationAddress": { "type": ["string", "null"], "pattern": "^0x[a-fA-F0-9]{40}$" },
          "standard": { "type": "string", "enum": ["ERC20", "ERC20-upgradeable"] },
          "status": { "type": "string", "enum": ["active", "paused", "disabled"], "default": "active" },
          "minter": { "type": "string", "pattern": "^0x[a-fA-F0-9]{40}$" },
          "governanceMode": { "type": "string", "enum": ["eoa"], "default": "eoa" }
        }
      },
      "ConsoleUser": {
        "type": "object",
        "required": ["userId", "issuerId", "email", "role", "status", "requireWebAuthn"],
        "properties": {
          "userId": { "type": "string" },
          "issuerId": { "type": "string" },
          "email": { "type": "string", "format": "email" },
          "role": { "$ref": "#/components/schemas/ConsoleRole" },
          "status": { "type": "string", "enum": ["invited", "active", "disabled"] },
          "requireWebAuthn": { "type": "boolean" },
          "lastLoginAt": { "type": ["string", "null"], "format": "date-time" },
          "invitedBy": { "type": ["string", "null"] },
          "invitedAt": { "type": ["string", "null"], "format": "date-time" },
          "createdAt": { "type": "string", "format": "date-time" },
          "updatedAt": { "type": "string", "format": "date-time" }
        }
      },
      "ApiKeyRecord": {
        "type": "object",
        "required": ["apiKey", "issuerId", "scopes", "environment", "isActive"],
        "properties": {
          "apiKey": { "type": "string" },
          "issuerId": { "type": "string" },
          "scopes": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/ApiScope" }
          },
          "environment": { "type": "string", "enum": ["sandbox", "production"] },
          "isActive": { "type": "boolean" },
          "createdAt": { "type": "string", "format": "date-time" },
          "updatedAt": { "type": "string", "format": "date-time" }
        }
      },
      "CreatedApiKey": {
        "allOf": [
          { "$ref": "#/components/schemas/ApiKeyRecord" },
          {
            "type": "object",
            "required": ["apiSecret"],
            "properties": {
              "apiSecret": { "type": "string" }
            }
          }
        ]
      },
      "CreateApiKeyRequest": {
        "type": "object",
        "properties": {
          "environment": { "type": "string", "enum": ["sandbox", "production"], "default": "sandbox" },
          "scopes": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/ApiScope" },
            "default": ["tokens.read", "issuances.read", "issuances.create", "redemptions.read", "redemptions.create"]
          }
        }
      },
      "InviteConsoleUserRequest": {
        "type": "object",
        "required": ["email", "role"],
        "properties": {
          "email": { "type": "string", "format": "email" },
          "role": { "$ref": "#/components/schemas/ConsoleRole" },
          "requireWebAuthn": { "type": "boolean", "default": false }
        }
      },
      "InvitedConsoleUserResponse": {
        "allOf": [
          { "$ref": "#/components/schemas/ConsoleUser" },
          {
            "type": "object",
            "properties": {
              "inviteEmail": {
                "type": "object",
                "properties": {
                  "delivered": { "type": "boolean" },
                  "messageId": { "type": "string" },
                  "reason": { "type": "string" }
                }
              }
            }
          }
        ]
      },
      "UpdateConsoleUserRequest": {
        "type": "object",
        "properties": {
          "role": { "$ref": "#/components/schemas/ConsoleRole" },
          "status": { "type": "string", "enum": ["invited", "active", "disabled"] },
          "requireWebAuthn": { "type": "boolean" }
        }
      },
      "Network": {
        "type": "string",
        "enum": ["polygon-amoy", "polygon"]
      },
      "OperationStatus": {
        "type": "string",
        "enum": ["unsigned", "submitted", "confirmed", "failed", "expired"]
      },
      "RedemptionStatus": {
        "type": "string",
        "enum": ["requested", "unsigned", "submitted", "confirmed", "failed", "expired"]
      },
      "ConsoleRole": {
        "type": "string",
        "enum": ["admin", "operator", "viewer"]
      },
      "ApiScope": {
        "type": "string",
        "enum": ["tokens.read", "issuances.read", "issuances.create", "redemptions.read", "redemptions.create"]
      },
      "Error": {
        "type": "object",
        "required": ["code", "message"],
        "properties": {
          "code": { "type": "string" },
          "message": { "type": "string" }
        }
      }
    }
  }
}
