{
  "openapi": "3.0.0",
  "info": {
    "title": "PayChainHQ External API",
    "version": "1.0.0",
    "description": "External business/public API contract for PayChainHQ integrators. Dashboard session, admin, internal, mock, and health routes are excluded.",
    "contact": {
      "name": "PayChain Support"
    }
  },
  "servers": [
    {
      "url": "https://api.paychainhq.io",
      "description": "Production server"
    }
  ],
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key",
        "description": "Business API key for authentication. Withdrawal creation requires a dedicated payout API key; standard API keys cannot move funds."
      },
      "AdminApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Admin-API-Key",
        "description": "Admin API key for authentication (SUPER_ADMIN role required for network/token management)"
      }
    },
    "schemas": {
      "WebhookEvent": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "eventType": {
            "type": "string",
            "example": "invoice.paid"
          },
          "url": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "sending",
              "retrying",
              "delivered",
              "failed"
            ]
          },
          "attempts": {
            "type": "integer"
          },
          "maxAttempts": {
            "type": "integer"
          },
          "failureReason": {
            "type": "string",
            "nullable": true
          },
          "latestRequestHeaders": {
            "type": "string",
            "nullable": true,
            "description": "Redacted JSON string of latest outbound headers."
          },
          "latestRequestBody": {
            "type": "string",
            "nullable": true,
            "description": "Latest raw webhook envelope sent to the merchant endpoint."
          },
          "latestResponseStatus": {
            "type": "integer",
            "nullable": true
          },
          "latestResponseBody": {
            "type": "string",
            "nullable": true,
            "description": "Latest response body snippet."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "WebhookEnvelope": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "event": {
            "type": "string",
            "example": "invoice.paid"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          },
          "data": {
            "type": "object"
          }
        }
      }
    }
  },
  "tags": [
    {
      "name": "Businesses",
      "description": "Business management endpoints"
    },
    {
      "name": "Invoices",
      "description": "Invoice creation and management"
    },
    {
      "name": "Customers",
      "description": "Customer management"
    },
    {
      "name": "Withdrawals",
      "description": "Withdrawal operations"
    },
    {
      "name": "Balances",
      "description": "Balance queries"
    },
    {
      "name": "Prices",
      "description": "Token price queries"
    }
  ],
  "paths": {
    "/api/v1/businesses/{id}/balances/total": {
      "get": {
        "summary": "Get total balance across all chains/networks/tokens",
        "description": "Returns NET balances (after fees are subtracted from invoice balances)",
        "tags": [
          "Balances"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Total balance summary"
          }
        }
      }
    },
    "/api/v1/businesses/{id}/balances": {
      "get": {
        "summary": "Get all balances for a business",
        "tags": [
          "Balances"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of all balances"
          }
        }
      }
    },
    "/api/v1/businesses/{id}": {
      "get": {
        "summary": "Get business details (business must authenticate as themselves)",
        "tags": [
          "Businesses"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Business details"
          },
          "404": {
            "description": "Business not found"
          }
        }
      }
    },
    "/api/v1/businesses/{id}/customers": {
      "post": {
        "summary": "Create a new customer with permanent blockchain addresses",
        "description": "Creates a customer record with permanent addresses (ETH, BTC, SOL).\nThese addresses are reused across all invoices for this customer.\nFor anonymous/one-off payments, create invoices without a customerId instead.\n",
        "tags": [
          "Customers"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Business ID"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "externalRef"
                ],
                "properties": {
                  "externalRef": {
                    "type": "string",
                    "description": "Your internal customer identifier (must be unique per business)",
                    "example": "customer_001"
                  },
                  "email": {
                    "type": "string",
                    "description": "Optional customer email address",
                    "example": "customer@example.com"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Customer created successfully with permanent addresses",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "externalRef": {
                      "type": "string"
                    },
                    "addresses": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "chainFamily": {
                            "type": "string",
                            "example": "eth"
                          },
                          "address": {
                            "type": "string",
                            "example": "0x..."
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request or business not properly configured"
          }
        }
      },
      "get": {
        "summary": "List all customers for a business",
        "tags": [
          "Customers"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of customers"
          }
        }
      }
    },
    "/api/v1/businesses/{id}/customers/{customerId}": {
      "get": {
        "summary": "Get a specific customer by ID",
        "tags": [
          "Customers"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "customerId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Customer details"
          },
          "404": {
            "description": "Customer not found"
          }
        }
      }
    },
    "/api/v1/businesses/{id}/invoices": {
      "post": {
        "summary": "Create a new invoice",
        "tags": [
          "Invoices"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Business ID"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "amount",
                  "token",
                  "chain"
                ],
                "properties": {
                  "customerId": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "amount": {
                    "type": "string",
                    "example": "100.50"
                  },
                  "token": {
                    "type": "string",
                    "example": "USDC"
                  },
                  "tokenMint": {
                    "type": "string",
                    "description": "Token contract/mint address (optional - uses registered address from DB if not provided). For EVM chains must be 0x + 40 hex chars.",
                    "example": "0x23559939bf42c4cbeb82ddc18c02804119ec4094"
                  },
                  "chain": {
                    "type": "string",
                    "enum": [
                      "eth",
                      "btc",
                      "sol"
                    ]
                  },
                  "networkId": {
                    "type": "string",
                    "example": "base-sepolia"
                  },
                  "expiresIn": {
                    "type": "number",
                    "description": "Expiration in seconds (300-604800)",
                    "example": 3600
                  },
                  "description": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Invoice created successfully"
          },
          "400": {
            "description": "Invalid request"
          }
        }
      },
      "get": {
        "summary": "List all invoices for a business (with pagination)",
        "tags": [
          "Invoices"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "status",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "number"
            }
          },
          {
            "in": "query",
            "name": "offset",
            "schema": {
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of invoices with pagination"
          }
        }
      }
    },
    "/api/v1/businesses/{id}/invoices/{invoiceId}": {
      "delete": {
        "summary": "Delete a pending invoice",
        "tags": [
          "Invoices"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "invoiceId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Invoice deleted"
          },
          "400": {
            "description": "Invoice cannot be deleted"
          },
          "404": {
            "description": "Invoice not found"
          }
        }
      }
    },
    "/api/v1/invoices/{invoiceId}": {
      "get": {
        "summary": "Public invoice lookup",
        "tags": [
          "Invoices"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "invoiceId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Raw-first public invoice payload",
            "content": {
              "application/json": {
                "example": {
                  "id": "46a1a472-ceed-460f-a981-20930b910e37",
                  "status": "confirming",
                  "state": "confirming",
                  "amount": {
                    "raw": "10000000",
                    "decimals": 6,
                    "display": "10",
                    "symbol": "USDC",
                    "chain": "eth",
                    "networkId": "base-mainnet"
                  },
                  "paidAmount": {
                    "raw": "10000000",
                    "decimals": 6,
                    "display": "10",
                    "symbol": "USDC",
                    "chain": "eth",
                    "networkId": "base-mainnet"
                  },
                  "usdSnapshot": {
                    "amount": "10.000000000000",
                    "price": "1.000000000000000000",
                    "pricedAt": "2026-02-26T10:00:00.000Z",
                    "source": "coingecko"
                  },
                  "usdNow": {
                    "amount": "10.000000000000",
                    "price": "1.000000000000000000",
                    "pricedAt": "2026-02-26T10:00:00.000Z",
                    "source": "coingecko",
                    "stale": false,
                    "ttlMs": 60000
                  }
                }
              }
            }
          },
          "404": {
            "description": "Invoice not found"
          }
        }
      }
    },
    "/api/v1/invoices/{invoiceId}/status": {
      "patch": {
        "summary": "Update invoice status (indexer/internal)",
        "tags": [
          "Invoices"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "invoiceId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Updated invoice in raw-first shape",
            "content": {
              "application/json": {
                "example": {
                  "id": "46a1a472-ceed-460f-a981-20930b910e37",
                  "status": "paid",
                  "state": "completed",
                  "paidAmount": {
                    "raw": "10000000",
                    "decimals": 6,
                    "display": "10",
                    "symbol": "USDC",
                    "chain": "eth",
                    "networkId": "base-mainnet"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/networks": {
      "get": {
        "summary": "List supported networks with valid networkId values and payload examples",
        "tags": [
          "Networks"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "chainFamily",
            "schema": {
              "type": "string",
              "example": "eth"
            },
            "description": "Optional chain family filter (eth, sol, btc, ...)"
          },
          {
            "in": "query",
            "name": "networkType",
            "schema": {
              "type": "string",
              "enum": [
                "MAINNET",
                "TESTNET",
                "DEVNET"
              ]
            }
          },
          {
            "in": "query",
            "name": "includeInactive",
            "schema": {
              "type": "boolean",
              "default": false
            },
            "description": "Include inactive networks when true"
          }
        ],
        "responses": {
          "200": {
            "description": "Supported network list"
          }
        }
      }
    },
    "/api/v1/networks/supported": {
      "get": {
        "summary": "Alias of /api/v1/networks",
        "tags": [
          "Networks"
        ],
        "responses": {
          "200": {
            "description": "Supported network list"
          }
        }
      }
    },
    "/api/v1/businesses/{id}/payment-methods": {
      "get": {
        "summary": "List deposit rails enabled for a business",
        "tags": [
          "Businesses"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Business-scoped payment methods"
          }
        }
      }
    },
    "/api/v1/prices/{symbol}": {
      "get": {
        "summary": "Get current price for a single token",
        "tags": [
          "Prices"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "symbol",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "USDC"
          }
        ],
        "responses": {
          "200": {
            "description": "Token price in USD"
          },
          "404": {
            "description": "Token not found"
          }
        }
      }
    },
    "/api/v1/prices": {
      "get": {
        "summary": "Get current prices for all supported tokens",
        "tags": [
          "Prices"
        ],
        "responses": {
          "200": {
            "description": "Token prices in USD"
          }
        }
      }
    },
    "/api/v1/supported-assets": {
      "get": {
        "summary": "List supported deposit networks and assets",
        "tags": [
          "Networks"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "includeInactive",
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Supported asset catalog"
          }
        }
      }
    },
    "/api/v1/tokens": {
      "get": {
        "summary": "List all active supported tokens for a specific chain/network",
        "tags": [
          "Tokens"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "in": "query",
            "name": "chain",
            "schema": {
              "type": "string",
              "enum": [
                "eth",
                "btc",
                "sol"
              ]
            }
          },
          {
            "in": "query",
            "name": "networkId",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of supported tokens"
          }
        }
      }
    },
    "/api/v1/businesses/{id}/transactions": {
      "get": {
        "summary": "Get unified transaction history (invoices + withdrawals)",
        "tags": [
          "Transactions"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "page",
            "schema": {
              "type": "number",
              "default": 1
            }
          },
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "number",
              "default": 20,
              "maximum": 100
            }
          },
          {
            "in": "query",
            "name": "type",
            "schema": {
              "type": "string",
              "enum": [
                "invoice",
                "withdrawal",
                "all"
              ],
              "default": "all"
            }
          },
          {
            "in": "query",
            "name": "chain",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "token",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "status",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "startDate",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "in": "query",
            "name": "endDate",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Transaction history with pagination"
          }
        }
      }
    },
    "/api/v1/businesses/{id}/withdrawals/quote": {
      "get": {
        "summary": "Get a quote for a withdrawal (preview costs and options)",
        "tags": [
          "Withdrawals"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "amount",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "token",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "chain",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "eth",
                "btc",
                "sol"
              ]
            }
          },
          {
            "in": "query",
            "name": "networkId",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Withdrawal quote details"
          }
        }
      }
    },
    "/api/v1/businesses/{id}/withdrawals": {
      "post": {
        "summary": "Create a new withdrawal request with dashboard step-up or a payout API key",
        "description": "Standard API keys cannot create withdrawals. Programmatic withdrawal creation requires a dedicated payout API key that passes destination, token, network, amount, daily spend, and optional IP policy.",
        "tags": [
          "Withdrawals"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "header",
            "name": "Idempotency-Key",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Stable replay-safe key for this intended withdrawal."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "amount",
                  "token",
                  "chain",
                  "destination"
                ],
                "properties": {
                  "amount": {
                    "type": "string",
                    "example": "100.50"
                  },
                  "token": {
                    "type": "string",
                    "example": "USDC"
                  },
                  "chain": {
                    "type": "string",
                    "enum": [
                      "eth",
                      "btc",
                      "sol"
                    ]
                  },
                  "networkId": {
                    "type": "string",
                    "example": "base-sepolia"
                  },
                  "destination": {
                    "type": "string",
                    "example": "0x..."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Withdrawal accepted and queued for processing"
          },
          "400": {
            "description": "Invalid request or insufficient balance"
          },
          "403": {
            "description": "Standard API key blocked or payout API key policy rejected"
          }
        }
      },
      "get": {
        "summary": "List all withdrawals for a business (with pagination)",
        "tags": [
          "Withdrawals"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "number"
            }
          },
          {
            "in": "query",
            "name": "offset",
            "schema": {
              "type": "number"
            }
          },
          {
            "in": "query",
            "name": "status",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of withdrawals"
          }
        }
      }
    },
    "/api/v1/businesses/{id}/withdrawals/{withdrawalId}": {
      "get": {
        "summary": "Get a specific withdrawal by ID",
        "tags": [
          "Withdrawals"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "withdrawalId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Withdrawal details"
          },
          "404": {
            "description": "Withdrawal not found"
          }
        }
      }
    },
    "/api/v1/businesses/{id}/withdrawals/{withdrawalId}/cancel": {
      "patch": {
        "summary": "Cancel a pending withdrawal",
        "tags": [
          "Withdrawals"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "withdrawalId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Withdrawal cancelled successfully"
          },
          "400": {
            "description": "Withdrawal cannot be cancelled"
          },
          "404": {
            "description": "Withdrawal not found"
          }
        }
      }
    }
  },
  "x-generatedAt": "2026-05-14T16:47:31.697Z"
}