{
  "openapi": "3.0.0",
  "paths": {
    "/pub/v2/rate": {
      "get": {
        "description": "Returns the latest active onramp and offramp rates for the given target fiat currency. Rates already include the business fee applied to the API key.",
        "operationId": "PubV2Controller_getRates",
        "parameters": [
          {
            "name": "currency",
            "required": true,
            "in": "query",
            "description": "Target fiat currency to fetch the on/off ramp rates for",
            "schema": {
              "example": "NGN",
              "enum": [
                "NGN",
                "GHS",
                "TZS",
                "KES",
                "ZAR",
                "USD"
              ],
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The onramp and offramp rates for the currency",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RatesDto"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key (x-api-key header)"
          },
          "404": {
            "description": "No active rate found for the currency"
          }
        },
        "security": [
          {
            "x-api-key": []
          }
        ],
        "summary": "Get on/off ramp rates for a currency",
        "tags": [
          "Pub V2"
        ]
      }
    },
    "/pub/v2/bank": {
      "get": {
        "description": "Returns the supported banks. Any of the query parameters may be used to narrow the list — code matches exactly, name and country match case-insensitively on a substring. With no parameters every bank is returned.",
        "operationId": "PubV2Controller_getBanks",
        "parameters": [
          {
            "name": "code",
            "required": false,
            "in": "query",
            "description": "Bank code to match exactly",
            "schema": {
              "example": "000016",
              "type": "string"
            }
          },
          {
            "name": "name",
            "required": false,
            "in": "query",
            "description": "Partial, case-insensitive bank name to match",
            "schema": {
              "example": "access",
              "type": "string"
            }
          },
          {
            "name": "country",
            "required": false,
            "in": "query",
            "description": "Country short code the bank operates in",
            "schema": {
              "example": "NG",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The matching banks",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/BankResDto"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key (x-api-key header)"
          }
        },
        "security": [
          {
            "x-api-key": []
          }
        ],
        "summary": "Get banks",
        "tags": [
          "Pub V2"
        ]
      }
    },
    "/pub/v2/bank-account": {
      "get": {
        "description": "Looks up a single bank account by its on-chain address or its account number. Provide at least one of the two query parameters.",
        "operationId": "PubV2Controller_searchBankAccount",
        "parameters": [
          {
            "name": "address",
            "required": false,
            "in": "query",
            "description": "Bank account on-chain address",
            "schema": {
              "example": "FsXpXq8wVTM13NDXxrf2X6PpgJWg9jCxRVuHhtfnojUa",
              "type": "string"
            }
          },
          {
            "name": "accountNumber",
            "required": false,
            "in": "query",
            "description": "User's bank account number",
            "schema": {
              "example": "0025635480",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The matching bank account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BankAccountDto"
                }
              }
            }
          },
          "400": {
            "description": "Neither an address nor an account number was provided"
          },
          "401": {
            "description": "Missing or invalid API key (x-api-key header)"
          },
          "404": {
            "description": "No bank account matched the query"
          }
        },
        "security": [
          {
            "x-api-key": []
          }
        ],
        "summary": "Search for a bank account",
        "tags": [
          "Pub V2"
        ]
      },
      "post": {
        "description": "Registers a bank account for the API key’s business using a bank code and account number. The account is confirmed with the provider and an on-chain address is assigned. If the account already exists it is returned as-is.",
        "operationId": "PubV2Controller_registerBankAccount",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RegisterBankAccountDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The registered bank account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BankAccountDto"
                }
              }
            }
          },
          "400": {
            "description": "Unknown bank code, or the account number could not be confirmed"
          },
          "401": {
            "description": "Missing or invalid API key (x-api-key header)"
          }
        },
        "security": [
          {
            "x-api-key": []
          }
        ],
        "summary": "Register a bank account",
        "tags": [
          "Pub V2"
        ]
      }
    },
    "/pub/v2/offramp": {
      "post": {
        "description": "Opens an offramp order that pays out to a bank account identified by bank code and account number. The account is confirmed with the provider first, so an unknown bank code or an account number that cannot be resolved is rejected before any order exists. When a bvn is given the order is credited to that end user — traced through their KYC record, or verified and registered if the BVN is new; without one the order is not attributed to any user. Provide either amount (token) or fiatAmount, not both. The returned address is where the tokens should be sent to fund the order.",
        "operationId": "PubV2Controller_createOfframpOrder",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateOfframpOrderDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created offramp order",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TransactionInterfaceDto"
                }
              }
            }
          },
          "400": {
            "description": "The bvn could not be verified, the account number could not be confirmed, or the order failed validation"
          },
          "401": {
            "description": "Missing or invalid API key (x-api-key header)"
          },
          "404": {
            "description": "Unknown bank code"
          }
        },
        "security": [
          {
            "x-api-key": []
          }
        ],
        "summary": "Create an offramp order",
        "tags": [
          "Pub V2"
        ]
      }
    },
    "/pub/v2/onramp": {
      "post": {
        "description": "Opens an onramp order for the API key’s business and returns the virtual bank account the end user pays into. Provide either amount (token) or fiatAmount, not both. When a bvn is given the order is credited to that end user — traced through their KYC record, or verified and registered if the BVN is new; without one the order is not attributed to any user. Once the deposit is confirmed the token is delivered to recipient on the given chain, and every status change is posted to webhookURL.",
        "operationId": "PubV2Controller_createOnrampOrder",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateOnrampOrderV2Dto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created onramp order. accountNumber, accountName and bank are the account to pay; the order stays in INIT until the deposit lands.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TransactionInterfaceDto"
                }
              }
            }
          },
          "400": {
            "description": "The bvn could not be verified, both or neither of amount and fiatAmount were given, or the order failed validation"
          },
          "401": {
            "description": "Missing or invalid API key (x-api-key header)"
          }
        },
        "security": [
          {
            "x-api-key": []
          }
        ],
        "summary": "Create an onramp order",
        "tags": [
          "Pub V2"
        ]
      }
    },
    "/pub/v2/transaction/{id}": {
      "get": {
        "description": "Looks up an onramp or offramp order by the id returned when it was created, at any stage from INIT through to COMPLETED or ERROR. Only orders belonging to the API key’s business are found; any other id returns 404.",
        "operationId": "PubV2Controller_getTransaction",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "The id returned when the order was created",
            "schema": {
              "example": "68ff3a4ee9834db8927f61ed",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The transaction in its current state",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TransactionInterfaceDto"
                }
              }
            }
          },
          "400": {
            "description": "The id is not a valid id"
          },
          "401": {
            "description": "Missing or invalid API key (x-api-key header)"
          },
          "404": {
            "description": "No transaction with that id belongs to your business"
          }
        },
        "security": [
          {
            "x-api-key": []
          }
        ],
        "summary": "Get a transaction",
        "tags": [
          "Pub V2"
        ]
      }
    },
    "/pub/v2/payment": {
      "post": {
        "description": "Opens a payment collected on behalf of the API key’s business and returns the funding instructions. method defaults to TOKEN, which requires chain and mint and returns the deposit address under token. For method=FIAT a currency must be supplied and the response carries the virtual account details under fiat. The payment stays AWAITING until the funds land, and a platform fee is charged on top of amount — the payer funds totalAmount and your balance is credited the amount share. Once it settles the payment is posted to the paymentWebhookURL configured on your API keys.",
        "operationId": "PubV2Controller_createPayment",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreatePaymentV2Dto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The pending payment with funding instructions (fiat or token).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "The amount was not positive, or the method was missing its currency (FIAT) or chain and mint (TOKEN)"
          },
          "401": {
            "description": "Missing or invalid API key (x-api-key header)"
          }
        },
        "security": [
          {
            "x-api-key": []
          }
        ],
        "summary": "Create a payment",
        "tags": [
          "Pub V2"
        ]
      }
    },
    "/pub/v2/webhook": {
      "patch": {
        "description": "Points the webhooks on the API key used for this request somewhere new. rampWebhookURL receives onramp and offramp order updates; paymentWebhookURL receives settled payments. Only the fields you send are changed, and sending one as an empty string clears it, stopping those deliveries. The full configuration after the change is returned.",
        "operationId": "PubV2Controller_updateWebhooks",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateWebhookDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The webhook configuration now on the key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookConfigDto"
                }
              }
            }
          },
          "400": {
            "description": "Neither URL was provided, or one of them is not a valid URL"
          },
          "401": {
            "description": "Missing or invalid API key (x-api-key header)"
          }
        },
        "security": [
          {
            "x-api-key": []
          }
        ],
        "summary": "Update your webhook URLs",
        "tags": [
          "Pub V2"
        ]
      }
    },
    "/pub/v2/webhook/test": {
      "post": {
        "description": "Posts a sample payload to one of the webhooks configured on the API key used for this request, and reports whether it was accepted. type=RAMP, the default, posts an order update to rampWebhookURL; type=PAYMENT posts a settled payment to paymentWebhookURL. Either payload has the same shape as the real thing, so a webhook that handles this call handles production traffic. Nothing is created and no order or payment is affected. A webhook that rejects the call still returns 200 — the failure is described in the response body.",
        "operationId": "PubV2Controller_testWebhook",
        "parameters": [
          {
            "name": "type",
            "required": false,
            "in": "query",
            "description": "Which webhook to exercise. `RAMP` posts a sample order to `rampWebhookURL`; `PAYMENT` posts a sample settled payment to `paymentWebhookURL`.",
            "schema": {
              "default": "RAMP",
              "enum": [
                "RAMP",
                "PAYMENT"
              ],
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The outcome of the delivery attempt",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookTestResultDto"
                }
              }
            }
          },
          "400": {
            "description": "The API key has no URL configured for the requested webhook"
          },
          "401": {
            "description": "Missing or invalid API key (x-api-key header)"
          }
        },
        "security": [
          {
            "x-api-key": []
          }
        ],
        "summary": "Test your webhook",
        "tags": [
          "Pub V2"
        ]
      }
    }
  },
  "info": {
    "title": "Paj Public API",
    "description": "Programmatic access to Paj's on/off ramp. Register a Nigerian bank account, get\nthe on-chain address that pays it, create offramp orders for a specific amount,\nand read the live conversion rates.\n\nEvery request must carry an `x-api-key` header. Keys are scoped to a business,\nand the rates you receive already have that business's fee applied.",
    "version": "2.0.0",
    "contact": {}
  },
  "tags": [],
  "servers": [
    {
      "url": "https://api.paj.cash",
      "description": "Production"
    }
  ],
  "components": {
    "securitySchemes": {
      "x-api-key": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key"
      }
    },
    "schemas": {
      "RateDto": {
        "type": "object",
        "properties": {
          "rate": {
            "type": "number",
            "description": "Exchange rate for base to target currency",
            "example": 1650
          },
          "type": {
            "type": "string",
            "description": "Rate type, either onRamp or offRamp",
            "enum": [
              "onRamp",
              "offRamp"
            ],
            "example": "offRamp"
          },
          "id": {
            "type": "string",
            "description": "Rate Id",
            "example": "673a42e547e586bcd37fb0d7"
          },
          "baseCurrency": {
            "type": "string",
            "description": "Base Currency, default is USD",
            "enum": [
              "NGN",
              "GHS",
              "TZS",
              "KES",
              "ZAR",
              "USD"
            ],
            "example": "USD"
          },
          "targetCurrency": {
            "type": "string",
            "description": "Merchant Trading currency",
            "example": "NGN"
          },
          "createdAt": {
            "format": "date-time",
            "type": "string",
            "description": "Date rate was created",
            "example": 1790607327438
          }
        },
        "required": [
          "rate",
          "type",
          "id",
          "baseCurrency",
          "targetCurrency",
          "createdAt"
        ]
      },
      "RatesDto": {
        "type": "object",
        "properties": {
          "onRampRate": {
            "$ref": "#/components/schemas/RateDto"
          },
          "offRampRate": {
            "$ref": "#/components/schemas/RateDto"
          }
        },
        "required": [
          "onRampRate",
          "offRampRate"
        ]
      },
      "BankResDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Bank id",
            "example": "Bank of America"
          },
          "name": {
            "type": "string",
            "description": "Bank name",
            "example": "Bank of America"
          },
          "country": {
            "type": "string",
            "description": "Country of the bank",
            "example": "NG"
          },
          "code": {
            "type": "string",
            "description": "Bank code",
            "example": "001"
          },
          "logo": {
            "type": "string",
            "description": "Bank logo URL",
            "example": "https://example.com/logo.png",
            "nullable": true
          }
        },
        "required": [
          "id",
          "name",
          "country",
          "code"
        ]
      },
      "BankAccountDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "id",
            "example": "someRANDOMid"
          },
          "accountName": {
            "type": "string",
            "description": "User's bank acount name",
            "example": "John Doe"
          },
          "accountNumber": {
            "type": "string",
            "description": "User's bank account number",
            "example": "0025635480"
          },
          "bank": {
            "type": "string",
            "description": "Bank name",
            "example": "First Bank"
          },
          "address": {
            "type": "string",
            "description": "Bank Account Address",
            "example": "FsXpXq8wVTM13NDXxrf2X6PpgJWg9jCxRVuHhtfnojUa"
          }
        },
        "required": [
          "id",
          "accountName",
          "accountNumber",
          "bank",
          "address"
        ]
      },
      "RegisterBankAccountDto": {
        "type": "object",
        "properties": {
          "bankCode": {
            "type": "string",
            "description": "Bank's code",
            "example": "000016"
          },
          "accountNumber": {
            "type": "string",
            "description": "User's bank account number",
            "example": "0025635480"
          }
        },
        "required": [
          "bankCode",
          "accountNumber"
        ]
      },
      "CreateOfframpOrderDto": {
        "type": "object",
        "properties": {
          "accountNumber": {
            "type": "string",
            "description": "Account Number to receive funds",
            "example": "679be2527f99f556d77b8cc2"
          },
          "currency": {
            "type": "object",
            "description": "Currency to receive funds",
            "example": "NGN"
          },
          "amount": {
            "type": "number",
            "description": "Token amount (provide either amount or fiatAmount, not both)",
            "example": 100
          },
          "fiatAmount": {
            "type": "number",
            "description": "Fiat amount (provide either amount or fiatAmount, not both)",
            "example": 100
          },
          "chain": {
            "type": "object",
            "description": "Chain",
            "example": "SOLANA",
            "default": "SOLANA"
          },
          "webhookURL": {
            "type": "string",
            "description": "Callback webhook url",
            "example": "https://mydomain.com/webhook"
          },
          "saveBeneficiary": {
            "type": "boolean",
            "description": "Save beneficiary",
            "example": true
          },
          "description": {
            "type": "string",
            "description": "Payment description",
            "example": "Payment for order #123"
          },
          "businessUSDCFee": {
            "type": "number",
            "description": "Business fee",
            "example": 0.5,
            "default": 0
          },
          "bankCode": {
            "type": "string",
            "description": "Code of the bank that receives the payout",
            "example": "000016"
          },
          "mint": {
            "type": "string",
            "description": "Mint address of the token being sold, on the given chain. On TON, a jetton master address or \"TON\" for Toncoin.",
            "example": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
          },
          "bvn": {
            "type": "string",
            "description": "BVN of the end user the order is created for. It is traced to that user's KYC record; if the BVN is not on file it is verified and the user is registered before the order is opened.",
            "example": "22222222222"
          }
        },
        "required": [
          "accountNumber",
          "currency",
          "bankCode",
          "mint"
        ]
      },
      "TransactionInterfaceDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Transaction Id",
            "example": "679be2527f99f556d77b8cc2"
          },
          "address": {
            "type": "string",
            "description": "Address",
            "example": "4KSLE7EU1P7PQ8Rc4hdb2ZKq2JmWHD8UXJp7guEdyT9j"
          },
          "signature": {
            "type": "string",
            "description": "Transaction Signature",
            "example": "2oLx7XrXP7Df9dZca1difJ91ZFmA44EEadNqrZTecta6ugcX8BAETbpvKQs54r58fmJTQVLzUSuzvRBFwS1GfJLo"
          },
          "mint": {
            "type": "string",
            "description": "Mint Address",
            "example": "So11111111111111111111111111111111111111112"
          },
          "currency": {
            "type": "object",
            "description": "Currency",
            "example": "NGN"
          },
          "amount": {
            "type": "number",
            "description": "Mint Amount",
            "example": 100000
          },
          "usdcAmount": {
            "type": "number",
            "description": "Usdc Amount",
            "example": 100
          },
          "fiatAmount": {
            "type": "number",
            "description": "Fiat Amount",
            "example": 100
          },
          "sender": {
            "type": "string",
            "description": "Wallet Public Key",
            "example": "4KSLE7EU1P7PQ8Rc4hdb2ZKq2JmWHD8UXJp7guEdyT9j"
          },
          "recipient": {
            "type": "string",
            "description": "Recipient Public Key",
            "example": "4KSLE7EU1P7PQ8Rc4hdb2ZKq2JmWHD8UXJp7guEdyT9j"
          },
          "rate": {
            "type": "number",
            "description": "Rate of currency to usdc",
            "example": 9
          },
          "status": {
            "type": "object",
            "description": "Transaction Status",
            "example": "PENDDING"
          },
          "transactionType": {
            "type": "object",
            "description": "Transaction Type",
            "example": "OFF_RAMP"
          },
          "createdAt": {
            "format": "date-time",
            "type": "string",
            "description": "Transaction creation date",
            "example": "2026-09-28T14:55:27.097Z"
          },
          "accountNumber": {
            "type": "string",
            "description": "Account Number (for onramp transactions)",
            "example": "0025635480"
          },
          "accountName": {
            "type": "string",
            "description": "Account Name (for onramp transactions)",
            "example": "Paj Inc."
          },
          "bank": {
            "type": "string",
            "description": "Bank Name (for onramp transactions)",
            "example": "First Bank"
          },
          "fee": {
            "type": "number",
            "description": "Transaction fee in usdc",
            "example": 0.2
          },
          "chain": {
            "type": "object",
            "description": "Chain",
            "example": "SOLANA"
          }
        },
        "required": [
          "id",
          "address",
          "signature",
          "mint",
          "currency",
          "amount",
          "usdcAmount",
          "fiatAmount",
          "sender",
          "recipient",
          "rate",
          "status",
          "transactionType",
          "createdAt",
          "accountNumber",
          "accountName",
          "bank",
          "fee",
          "chain"
        ]
      },
      "CreateOnrampOrderV2Dto": {
        "type": "object",
        "properties": {
          "fiatAmount": {
            "type": "number",
            "description": "Amount user should doposite; in local currency. Mutually exclusive with amount.",
            "example": 100
          },
          "amount": {
            "type": "number",
            "description": "Amount of token user expected to receive. Mutually exclusive with fiatAmount."
          },
          "currency": {
            "type": "object",
            "description": "Local currency user wish to use",
            "example": "NGN"
          },
          "recipient": {
            "type": "string",
            "description": "User wallet address",
            "example": "4C8XFXBPG1Dm2eBs68oixjsVEzjxz5pwTDrLCR17ZRGz"
          },
          "mint": {
            "type": "string",
            "description": "Expected token mint address. On TON, a jetton master address or \"TON\" for Toncoin.",
            "example": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
          },
          "chain": {
            "type": "object",
            "description": "Wallet Chain",
            "example": "SOLANA"
          },
          "webhookURL": {
            "type": "string",
            "description": "Webhook URL to receive order status updates",
            "example": "https://example.com/webhook"
          },
          "userExternalId": {
            "type": "string",
            "description": "User external id",
            "example": "user-123"
          },
          "businessUSDCFee": {
            "type": "number",
            "description": "Business fee in USDC deducted from the token amount sent to the user",
            "example": 0.5,
            "default": 0
          },
          "bvn": {
            "type": "string",
            "description": "BVN of the end user the order is created for. It is traced to that user's KYC record; if the BVN is not on file it is verified and the user is registered before the order is opened.",
            "example": "22222222222"
          }
        },
        "required": [
          "currency",
          "recipient",
          "mint",
          "chain"
        ]
      },
      "CreatePaymentV2Dto": {
        "type": "object",
        "properties": {
          "amount": {
            "type": "number",
            "description": "Amount to collect, in whole units. Must be positive.",
            "example": 100
          },
          "currency": {
            "type": "string",
            "enum": [
              "NGN",
              "GHS",
              "TZS",
              "KES",
              "ZAR",
              "USD"
            ],
            "description": "Fiat currency to charge. Required when `method=FIAT`; ignored for `TOKEN`."
          },
          "chain": {
            "type": "string",
            "enum": [
              "SOLANA",
              "MONAD",
              "ETHEREUM",
              "BSC",
              "BASE",
              "ARC",
              "ZCASH",
              "TON"
            ],
            "description": "Chain to receive the token on. Required when `method=TOKEN`; ignored for `FIAT`."
          },
          "mint": {
            "type": "string",
            "description": "Mint/contract address of the token on the selected `chain`. Required when `method=TOKEN`.",
            "example": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
          },
          "method": {
            "type": "string",
            "enum": [
              "FIAT",
              "TOKEN"
            ],
            "default": "TOKEN",
            "description": "How the payment is funded. Defaults to `TOKEN`, which issues a deposit address and requires `chain` and `mint`. `FIAT` issues a virtual account and requires `currency`."
          }
        },
        "required": [
          "amount"
        ]
      },
      "PaymentFiatDto": {
        "type": "object",
        "properties": {
          "currency": {
            "type": "string",
            "enum": [
              "NGN",
              "GHS",
              "TZS",
              "KES",
              "ZAR",
              "USD"
            ]
          },
          "accountNumber": {
            "type": "string",
            "description": "Virtual account number to fund the payment."
          },
          "accountName": {
            "type": "string",
            "description": "Name on the virtual account."
          },
          "bank": {
            "description": "The bank the virtual account belongs to.",
            "allOf": [
              {
                "$ref": "#/components/schemas/BankResDto"
              }
            ]
          }
        },
        "required": [
          "currency",
          "accountNumber"
        ]
      },
      "PaymentTokenDto": {
        "type": "object",
        "properties": {
          "chain": {
            "type": "string",
            "enum": [
              "SOLANA",
              "MONAD",
              "ETHEREUM",
              "BSC",
              "BASE",
              "ARC",
              "ZCASH",
              "TON"
            ]
          },
          "mint": {
            "type": "string",
            "description": "USDC mint/contract address on the chain."
          },
          "address": {
            "type": "string",
            "description": "Deposit address to send USDC to."
          }
        },
        "required": [
          "chain",
          "mint",
          "address"
        ]
      },
      "PaymentResponseDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Payment id. Subscribe to live updates with it.",
            "example": "507f1f77bcf86cd799439011"
          },
          "method": {
            "type": "string",
            "enum": [
              "FIAT",
              "TOKEN"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "INIT",
              "AWAITING",
              "PROCESSING",
              "SUCCESSFUL",
              "EXPIRED",
              "ERROR"
            ]
          },
          "amount": {
            "type": "string",
            "description": "Requested amount, in whole units of the currency or token (the stored base units scaled back down by `details.decimals`).",
            "example": 100
          },
          "fee": {
            "type": "number",
            "description": "Platform fee charged on top of `amount`, in the same units. Absent when the payment carries no fee.",
            "example": 1
          },
          "totalAmount": {
            "type": "string",
            "description": "What the payer must fund: `amount` plus any `fee`. Equal to `amount` when no fee was charged.",
            "example": 101
          },
          "expiresAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the payment window closes."
          },
          "fiat": {
            "description": "Fiat funding details. Present when method=FIAT.",
            "allOf": [
              {
                "$ref": "#/components/schemas/PaymentFiatDto"
              }
            ]
          },
          "token": {
            "description": "On-chain funding details. Present when method=TOKEN.",
            "allOf": [
              {
                "$ref": "#/components/schemas/PaymentTokenDto"
              }
            ]
          }
        },
        "required": [
          "id",
          "method",
          "status",
          "amount",
          "totalAmount"
        ]
      },
      "UpdateWebhookDto": {
        "type": "object",
        "properties": {
          "rampWebhookURL": {
            "type": "string",
            "description": "Where onramp and offramp order updates are posted. Send an empty string to stop receiving them.",
            "example": "https://example.com/webhook"
          },
          "paymentWebhookURL": {
            "type": "string",
            "description": "Where settled payments are posted. Send an empty string to stop receiving them.",
            "example": "https://example.com/payment-webhook"
          }
        }
      },
      "WebhookConfigDto": {
        "type": "object",
        "properties": {
          "rampWebhookURL": {
            "type": "string",
            "description": "Where onramp and offramp order updates are posted.",
            "example": "https://example.com/webhook"
          },
          "paymentWebhookURL": {
            "type": "string",
            "description": "Where settled payments are posted.",
            "example": "https://example.com/payment-webhook"
          },
          "webhookSecret": {
            "type": "string",
            "description": "The secret every delivery to these URLs is signed with. Verify a delivery by computing HMAC-SHA256 over `{X-PAJ-Timestamp}.{raw body}` with this secret and comparing it, in constant time, against the hex digest in X-PAJ-Signature (after the `v1=` prefix). Reject deliveries whose timestamp is more than a few minutes old.",
            "example": "whsec_2f1c…"
          }
        }
      },
      "WebhookTestResultDto": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "description": "The webhook URL configured on the API key that was called",
            "example": "https://example.com/webhook"
          },
          "delivered": {
            "type": "boolean",
            "description": "True if the endpoint answered with a 2xx",
            "example": true
          },
          "durationMs": {
            "type": "number",
            "description": "Round trip time of the delivery attempt, in milliseconds",
            "example": 214
          },
          "error": {
            "type": "string",
            "description": "Why the delivery failed — the rejecting status code, or the transport error if the endpoint could not be reached at all. Absent when delivered.",
            "example": "Webhook call failed with status: 404"
          }
        },
        "required": [
          "url",
          "delivered",
          "durationMs"
        ]
      }
    }
  }
}
