{
  "openapi": "3.1.0",
  "info": {
    "title": "Chipi Pay API",
    "description": "REST API for creating self-custodial wallets, executing gasless transactions, and purchasing services on Starknet.",
    "version": "14.0.0",
    "license": {
      "name": "MIT"
    }
  },
  "servers": [
    {
      "url": "https://api.chipipay.com/v1"
    }
  ],
  "security": [
    {
      "bearerAuth": [],
      "apiKeyAuth": []
    }
  ],
  "tags": [
    {
      "name": "Wallets",
      "description": "Create and manage self-custodial Starknet wallets"
    },
    {
      "name": "Transactions",
      "description": "Record and query blockchain transactions"
    },
    {
      "name": "SKUs",
      "description": "Browse available services for purchase"
    },
    {
      "name": "SKU Purchases",
      "description": "Purchase services using blockchain payments"
    },
    {
      "name": "Exchanges",
      "description": "Currency conversion rates"
    }
  ],
  "paths": {
    "/chipi-wallets/by-user": {
      "get": {
        "tags": [
          "Wallets"
        ],
        "summary": "Get Wallet",
        "operationId": "getWallet",
        "description": "Retrieve a wallet by external user ID. Returns null (404) if no wallet exists for this user.",
        "parameters": [
          {
            "name": "externalUserId",
            "in": "query",
            "required": true,
            "description": "Your application's user identifier",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Wallet found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WalletResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Wallet not found for this user",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/chipi-wallets": {
      "post": {
        "tags": [
          "Wallets"
        ],
        "summary": "Create Wallet",
        "operationId": "createWallet",
        "description": "Create a new self-custodial wallet. This is the second step after calling `/chipi-wallets/prepare-creation` to get the typed data for signing.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateWalletBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Wallet created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WalletResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/chipi-wallets/prepare-creation": {
      "post": {
        "tags": [
          "Wallets"
        ],
        "summary": "Prepare Wallet Creation",
        "operationId": "prepareWalletCreation",
        "description": "Generate the typed data that the wallet owner must sign before creating the wallet. This is the first step in the wallet creation flow.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PrepareWalletCreationBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Typed data generated for signing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrepareWalletCreationResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/chipi-wallets/update-encryption-details": {
      "patch": {
        "tags": [
          "Wallets"
        ],
        "summary": "Update Wallet Encryption",
        "operationId": "updateWalletEncryption",
        "description": "Replace the stored encrypted private key after the user changes their passkey or PIN. The old encryption key will no longer work after this call.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateWalletEncryptionBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Encryption updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UpdateWalletEncryptionResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Wallet not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/chipi-wallets/token-balance": {
      "get": {
        "tags": [
          "Wallets"
        ],
        "summary": "Get Token Balance",
        "operationId": "getTokenBalance",
        "description": "Get the balance of a specific token for a wallet. Provide either `externalUserId` or `walletPublicKey` to identify the wallet.",
        "parameters": [
          {
            "name": "externalUserId",
            "in": "query",
            "description": "Your application's user identifier (provide this or walletPublicKey)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "walletPublicKey",
            "in": "query",
            "description": "The wallet's public key / address (provide this or externalUserId)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "chainToken",
            "in": "query",
            "required": true,
            "description": "The token to query",
            "schema": {
              "$ref": "#/components/schemas/ChainToken"
            }
          },
          {
            "name": "chain",
            "in": "query",
            "required": true,
            "description": "The blockchain network",
            "schema": {
              "$ref": "#/components/schemas/Chain"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Token balance",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TokenBalanceResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/transactions/record-send": {
      "post": {
        "tags": [
          "Transactions"
        ],
        "summary": "Record Send Transaction",
        "operationId": "recordSendTransaction",
        "description": "Record a token transfer transaction that was executed on-chain. This creates a transaction record in Chipi for tracking and history purposes.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RecordSendTransactionBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Transaction recorded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Transaction"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/transactions/transaction-list": {
      "get": {
        "tags": [
          "Transactions"
        ],
        "summary": "Get Transaction List",
        "operationId": "getTransactionList",
        "description": "Get paginated transaction history for a wallet. Can be filtered by date and function name.",
        "parameters": [
          {
            "name": "walletAddress",
            "in": "query",
            "required": true,
            "description": "The wallet address to get transactions for",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "in": "query",
            "description": "Page number (default: 1)",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Items per page (default: 10)",
            "schema": {
              "type": "integer",
              "default": 10
            }
          },
          {
            "name": "calledFunction",
            "in": "query",
            "description": "Filter by contract function name (e.g., \"transfer\", \"approve\")",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "day",
            "in": "query",
            "description": "Filter by day of month (1-31)",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 31
            }
          },
          {
            "name": "month",
            "in": "query",
            "description": "Filter by month (1-12)",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 12
            }
          },
          {
            "name": "year",
            "in": "query",
            "description": "Filter by year (e.g., 2025)",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated transaction list",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedTransactionResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/skus": {
      "get": {
        "tags": [
          "SKUs"
        ],
        "summary": "Get SKU List",
        "operationId": "getSkuList",
        "description": "Retrieve a paginated list of available services (SKUs) for purchase. Can be filtered by category and provider.",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "description": "Page number (default: 1)",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Items per page (default: 10)",
            "schema": {
              "type": "integer",
              "default": 10
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Number of items to skip",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "category",
            "in": "query",
            "description": "Filter by service category",
            "schema": {
              "$ref": "#/components/schemas/SkuCategory"
            }
          },
          {
            "name": "provider",
            "in": "query",
            "description": "Filter by provider ID",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of SKUs",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedSkuResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/skus/{id}": {
      "get": {
        "tags": [
          "SKUs"
        ],
        "summary": "Get SKU",
        "operationId": "getSku",
        "description": "Retrieve a single SKU by its ID.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The SKU identifier",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "SKU details",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Sku"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "SKU not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/sku-purchases": {
      "post": {
        "tags": [
          "SKU Purchases"
        ],
        "summary": "Create SKU Purchase",
        "operationId": "createSkuPurchase",
        "description": "Record a service purchase after the on-chain payment transaction has been executed. The transaction hash from the blockchain payment is required.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateSkuPurchaseBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Purchase recorded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Transaction"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/sku-purchases/{id}": {
      "get": {
        "tags": [
          "SKU Purchases"
        ],
        "summary": "Get SKU Purchase",
        "operationId": "getSkuPurchase",
        "description": "Retrieve a SKU purchase by its ID. Use this to check the status of a purchase after creation.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The SKU purchase identifier",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Purchase details",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Transaction"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Purchase not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/exchanges/usd-amount": {
      "get": {
        "tags": [
          "Exchanges"
        ],
        "summary": "Get USD Amount",
        "operationId": "getUsdAmount",
        "description": "Convert an amount from a supported currency (e.g., MXN) to USD using the current exchange rate.",
        "parameters": [
          {
            "name": "currencyAmount",
            "in": "query",
            "required": true,
            "description": "The amount in the source currency",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "currency",
            "in": "query",
            "required": true,
            "description": "The source currency code",
            "schema": {
              "$ref": "#/components/schemas/Currency"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The equivalent USD amount",
            "content": {
              "application/json": {
                "schema": {
                  "type": "number",
                  "description": "The converted USD amount",
                  "example": 13.42
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Chain": {
        "type": "string",
        "enum": [
          "STARKNET",
          "BASE",
          "ARBITRUM",
          "OPTIMISM",
          "ROOTSTOCK",
          "SCROLL",
          "WORLDCHAIN"
        ],
        "description": "Blockchain network identifier"
      },
      "ChainToken": {
        "type": "string",
        "enum": [
          "USDC",
          "USDC_E",
          "USDT",
          "DAI",
          "ETH",
          "STRK",
          "SLINK",
          "ALF",
          "BROTHER",
          "ARB",
          "DOC",
          "MXNB",
          "WLD",
          "WBTC",
          "OTHER"
        ],
        "description": "Token identifier"
      },
      "Currency": {
        "type": "string",
        "enum": [
          "MXN",
          "USD"
        ],
        "description": "Fiat currency code"
      },
      "WalletType": {
        "type": "string",
        "enum": [
          "CHIPI",
          "READY"
        ],
        "description": "Wallet account type. CHIPI uses OpenZeppelin account. READY uses Argent X account."
      },
      "SkuCategory": {
        "type": "string",
        "enum": [
          "TELEPEAJE",
          "TELEFONICA",
          "GENERAL",
          "TESORERIA",
          "LUZ",
          "INTERNET",
          "TV",
          "MOVILIDAD",
          "RECARGAS",
          "GIFT_CARDS",
          "GAMING",
          "VENTAS_CATALOGO",
          "DEPORTES",
          "STREAMING"
        ],
        "description": "Service category for SKUs"
      },
      "TxStatus": {
        "type": "string",
        "enum": [
          "PENDING",
          "PROCESSING",
          "SUCCESS",
          "FAILED",
          "CANCELLED"
        ],
        "description": "Transaction lifecycle status"
      },
      "WalletResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Internal wallet identifier"
          },
          "externalUserId": {
            "type": "string",
            "description": "Your application's user identifier"
          },
          "organizationId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Organization that owns this wallet"
          },
          "apiPublicKey": {
            "type": "string",
            "description": "The API key used to create this wallet"
          },
          "publicKey": {
            "type": "string",
            "description": "The wallet's Starknet address (account contract address)"
          },
          "normalizedPublicKey": {
            "type": "string",
            "description": "Lowercase, zero-padded version of the public key"
          },
          "encryptedPrivateKey": {
            "type": "string",
            "description": "AES-encrypted private key (decryptable only with the user's encrypt key)"
          },
          "isDeployed": {
            "type": "boolean",
            "description": "Whether the account contract has been deployed on-chain"
          },
          "walletType": {
            "$ref": "#/components/schemas/WalletType"
          },
          "chain": {
            "$ref": "#/components/schemas/Chain"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "externalUserId",
          "apiPublicKey",
          "publicKey",
          "normalizedPublicKey",
          "encryptedPrivateKey",
          "isDeployed",
          "walletType",
          "chain",
          "createdAt",
          "updatedAt"
        ]
      },
      "CreateWalletBody": {
        "type": "object",
        "properties": {
          "externalUserId": {
            "type": "string",
            "description": "Your application's user identifier"
          },
          "userId": {
            "type": "string",
            "description": "Optional secondary user identifier"
          },
          "publicKey": {
            "type": "string",
            "description": "The calculated account contract address"
          },
          "walletType": {
            "$ref": "#/components/schemas/WalletType"
          },
          "chain": {
            "$ref": "#/components/schemas/Chain"
          },
          "userSignature": {
            "type": "object",
            "description": "The typed data signature from the wallet owner",
            "properties": {
              "r": {
                "type": "string"
              },
              "s": {
                "type": "string"
              },
              "recovery": {
                "type": "integer"
              }
            },
            "required": [
              "r",
              "s",
              "recovery"
            ]
          },
          "typedData": {
            "type": "object",
            "description": "The typed data that was signed (from prepare-creation)"
          },
          "encryptedPrivateKey": {
            "type": "string",
            "description": "The private key encrypted with the user's encrypt key"
          },
          "deploymentData": {
            "type": "object",
            "description": "Contract deployment parameters",
            "properties": {
              "class_hash": {
                "type": "string"
              },
              "salt": {
                "type": "string"
              },
              "unique": {
                "type": "string"
              },
              "calldata": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            },
            "required": [
              "class_hash",
              "salt",
              "unique",
              "calldata"
            ]
          }
        },
        "required": [
          "externalUserId",
          "publicKey",
          "walletType",
          "chain",
          "userSignature",
          "typedData",
          "encryptedPrivateKey",
          "deploymentData"
        ]
      },
      "PrepareWalletCreationBody": {
        "type": "object",
        "properties": {
          "chain": {
            "$ref": "#/components/schemas/Chain"
          },
          "publicKey": {
            "type": "string",
            "description": "The calculated account contract address"
          },
          "walletType": {
            "$ref": "#/components/schemas/WalletType"
          },
          "starkKeyPubAX": {
            "type": "string",
            "description": "The Stark curve public key"
          }
        },
        "required": [
          "chain",
          "publicKey",
          "walletType",
          "starkKeyPubAX"
        ]
      },
      "PrepareWalletCreationResponse": {
        "type": "object",
        "properties": {
          "typedData": {
            "type": "object",
            "description": "EIP-712 style typed data to be signed by the wallet owner"
          },
          "accountClassHash": {
            "type": "string",
            "description": "The class hash for the account contract"
          }
        },
        "required": [
          "typedData",
          "accountClassHash"
        ]
      },
      "UpdateWalletEncryptionBody": {
        "type": "object",
        "properties": {
          "externalUserId": {
            "type": "string",
            "description": "Your application's user identifier"
          },
          "newEncryptedPrivateKey": {
            "type": "string",
            "description": "The private key re-encrypted with the new passkey or PIN"
          },
          "publicKey": {
            "type": "string",
            "description": "Wallet public key (required when the user has multiple wallets)"
          }
        },
        "required": [
          "externalUserId",
          "newEncryptedPrivateKey"
        ]
      },
      "UpdateWalletEncryptionResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "Whether the update succeeded"
          }
        }
      },
      "TokenBalanceResponse": {
        "type": "object",
        "properties": {
          "chain": {
            "$ref": "#/components/schemas/Chain"
          },
          "chainToken": {
            "$ref": "#/components/schemas/ChainToken"
          },
          "chainTokenAddress": {
            "type": "string",
            "description": "The token's contract address on the chain"
          },
          "decimals": {
            "type": "integer",
            "description": "Number of decimal places for the token"
          },
          "balance": {
            "type": "string",
            "description": "The raw balance (divide by 10^decimals for human-readable amount)"
          }
        },
        "required": [
          "chain",
          "chainToken",
          "chainTokenAddress",
          "decimals",
          "balance"
        ]
      },
      "Transaction": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique transaction identifier"
          },
          "type": {
            "type": "string",
            "description": "Transaction type"
          },
          "subType": {
            "type": "string",
            "description": "Transaction sub-type"
          },
          "chain": {
            "$ref": "#/components/schemas/Chain"
          },
          "transactionHash": {
            "type": "string",
            "description": "On-chain transaction hash"
          },
          "senderAddress": {
            "type": "string",
            "description": "The sender's wallet address"
          },
          "destinationAddress": {
            "type": "string",
            "description": "The recipient's wallet address"
          },
          "contractAddress": {
            "type": "string",
            "description": "The contract that was called"
          },
          "contractName": {
            "type": "string"
          },
          "functionName": {
            "type": "string",
            "description": "The contract function that was called"
          },
          "functionSelector": {
            "type": "string"
          },
          "token": {
            "$ref": "#/components/schemas/ChainToken"
          },
          "tokenAddress": {
            "type": "string",
            "description": "The token's contract address"
          },
          "amount": {
            "type": "string",
            "description": "Transfer amount (raw, divide by 10^decimals)"
          },
          "skuId": {
            "type": "string",
            "description": "SKU ID if this is a service purchase"
          },
          "skuReference": {
            "type": "string",
            "description": "Reference provided for the SKU purchase"
          },
          "skuPurchaseAmount": {
            "type": "number",
            "description": "Purchase amount in local currency"
          },
          "skuPurchaseCurrency": {
            "$ref": "#/components/schemas/Currency"
          },
          "skuFileNumber": {
            "type": "string",
            "description": "File number from the service provider"
          },
          "status": {
            "$ref": "#/components/schemas/TxStatus"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "confirmedAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "apiPublicKey": {
            "type": "string",
            "description": "The API key that created this transaction"
          },
          "chipiWalletId": {
            "type": "string",
            "description": "Internal wallet ID of the sender"
          },
          "destinationChipiWalletId": {
            "type": "string",
            "description": "Internal wallet ID of the recipient (if also a Chipi wallet)"
          }
        },
        "required": [
          "id",
          "chain",
          "transactionHash",
          "senderAddress",
          "destinationAddress",
          "status",
          "createdAt",
          "updatedAt",
          "apiPublicKey"
        ]
      },
      "RecordSendTransactionBody": {
        "type": "object",
        "properties": {
          "transactionHash": {
            "type": "string",
            "description": "The on-chain transaction hash"
          },
          "expectedSender": {
            "type": "string",
            "description": "The sender's wallet address"
          },
          "expectedRecipient": {
            "type": "string",
            "description": "The recipient's wallet address"
          },
          "expectedToken": {
            "$ref": "#/components/schemas/ChainToken"
          },
          "expectedAmount": {
            "type": "string",
            "description": "The expected transfer amount (human-readable, e.g., \"10.5\")"
          },
          "chain": {
            "$ref": "#/components/schemas/Chain"
          }
        },
        "required": [
          "transactionHash",
          "expectedSender",
          "expectedRecipient",
          "expectedToken",
          "expectedAmount",
          "chain"
        ]
      },
      "PaginatedTransactionResponse": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Transaction"
            }
          },
          "total": {
            "type": "integer",
            "description": "Total number of matching transactions"
          },
          "page": {
            "type": "integer",
            "description": "Current page number"
          },
          "limit": {
            "type": "integer",
            "description": "Items per page"
          },
          "totalPages": {
            "type": "integer",
            "description": "Total number of pages"
          }
        },
        "required": [
          "data",
          "total",
          "page",
          "limit",
          "totalPages"
        ]
      },
      "Sku": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique identifier for the SKU"
          },
          "providerId": {
            "type": "string",
            "description": "Provider identifier"
          },
          "name": {
            "type": "string",
            "description": "Display name of the service"
          },
          "description": {
            "type": "string",
            "description": "Detailed description of the service"
          },
          "category": {
            "$ref": "#/components/schemas/SkuCategory"
          },
          "referenceRegexValidation": {
            "type": "string",
            "description": "Regex pattern for validating reference input (e.g., phone number format)"
          },
          "referenceLabel": {
            "type": "string",
            "description": "Label for the reference field (e.g., \"Phone Number\")"
          },
          "amountRegexValidation": {
            "type": "string",
            "description": "Regex pattern for validating amount input"
          },
          "amountLabel": {
            "type": "string",
            "description": "Label for the amount field"
          },
          "fixedAmount": {
            "type": "number",
            "description": "Fixed amount for the service (omitted for variable amounts)"
          },
          "currency": {
            "$ref": "#/components/schemas/Currency"
          },
          "canCheckSkuReference": {
            "type": "boolean",
            "description": "Whether the reference can be validated before purchase"
          },
          "logoUrl": {
            "type": "string",
            "description": "URL to the service logo"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "providerId",
          "name",
          "category",
          "referenceRegexValidation",
          "referenceLabel",
          "amountRegexValidation",
          "amountLabel",
          "currency",
          "canCheckSkuReference"
        ]
      },
      "PaginatedSkuResponse": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Sku"
            }
          },
          "total": {
            "type": "integer",
            "description": "Total number of matching SKUs"
          },
          "page": {
            "type": "integer",
            "description": "Current page number"
          },
          "limit": {
            "type": "integer",
            "description": "Items per page"
          },
          "totalPages": {
            "type": "integer",
            "description": "Total number of pages"
          }
        },
        "required": [
          "data",
          "total",
          "page",
          "limit",
          "totalPages"
        ]
      },
      "CreateSkuPurchaseBody": {
        "type": "object",
        "properties": {
          "transactionHash": {
            "type": "string",
            "description": "The on-chain transaction hash from the payment"
          },
          "walletAddress": {
            "type": "string",
            "description": "The wallet address that made the payment"
          },
          "skuId": {
            "type": "string",
            "description": "The SKU ID of the service to purchase"
          },
          "skuReference": {
            "type": "string",
            "description": "Reference for the service (e.g., phone number, account number)"
          },
          "currencyAmount": {
            "type": "number",
            "description": "The amount in local currency (e.g., MXN)"
          },
          "chain": {
            "$ref": "#/components/schemas/Chain"
          },
          "token": {
            "$ref": "#/components/schemas/ChainToken"
          }
        },
        "required": [
          "transactionHash",
          "walletAddress",
          "skuId",
          "skuReference",
          "currencyAmount",
          "chain",
          "token"
        ]
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "description": "Error code"
          },
          "message": {
            "type": "string",
            "description": "Human-readable error message"
          }
        },
        "required": [
          "error",
          "message"
        ]
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Bad request",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Unauthorized — invalid or missing API key / bearer token",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Rate limit exceeded (100 requests/minute per API key)",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "Bearer token — use your secret key (sk_prod_xxx) for server-side calls, or the user's JWT for client-side calls"
      },
      "apiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key",
        "description": "Your Chipi public API key (pk_xxx)"
      }
    }
  }
}