{
  "openapi": "3.0.3",
  "info": {
    "title": "Domvex API",
    "version": "1.0.0",
    "description": "API REST Domvex para autenticação, saldo, Pix, checkout de cartão, boleto, saques e pagamentos por QR Code. Use HTTPS e mantenha as credenciais no backend da sua aplicação. A especificação contém somente operações disponíveis no serviço."
  },
  "servers": [
    {
      "url": "https://api.domvexbr.com",
      "description": "API Domvex"
    }
  ],
  "tags": [
    {
      "name": "Autenticação"
    },
    {
      "name": "Conta"
    },
    {
      "name": "Pagamentos"
    },
    {
      "name": "Saques"
    }
  ],
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT"
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string"
          },
          "request_id": {
            "type": "string"
          }
        }
      },
      "Payer": {
        "type": "object",
        "required": [
          "name",
          "email",
          "document"
        ],
        "properties": {
          "name": {
            "type": "string",
            "example": "João da Silva"
          },
          "email": {
            "type": "string",
            "format": "email",
            "example": "joao@exemplo.com"
          },
          "document": {
            "type": "string",
            "description": "CPF ou CNPJ do pagador.",
            "example": "12345678901"
          }
        }
      },
      "BoletoAddress": {
        "type": "object",
        "required": [
          "street_name",
          "street_number",
          "neighborhood",
          "city",
          "state",
          "zip_code"
        ],
        "properties": {
          "street_name": {
            "type": "string"
          },
          "street_number": {
            "type": "string"
          },
          "neighborhood": {
            "type": "string"
          },
          "city": {
            "type": "string"
          },
          "state": {
            "type": "string",
            "minLength": 2,
            "maxLength": 2,
            "example": "SC"
          },
          "zip_code": {
            "type": "string",
            "pattern": "^\\d{8}$",
            "example": "88010000"
          }
        }
      },
      "LoginRequest": {
        "type": "object",
        "required": [
          "client_id",
          "client_secret"
        ],
        "properties": {
          "client_id": {
            "type": "string"
          },
          "client_secret": {
            "type": "string",
            "writeOnly": true
          }
        }
      },
      "LoginResponse": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string"
          },
          "token": {
            "type": "string"
          },
          "user": {
            "type": "object",
            "properties": {
              "id": {
                "type": "integer"
              },
              "name": {
                "type": "string"
              },
              "email": {
                "type": "string",
                "format": "email"
              },
              "client_id": {
                "type": "string"
              }
            }
          }
        }
      },
      "BalanceResponse": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string"
          },
          "total_balance": {
            "type": "number",
            "format": "double"
          },
          "balance": {
            "type": "number",
            "format": "double",
            "description": "Saldo disponível."
          },
          "balance_block": {
            "type": "number",
            "format": "double",
            "description": "Saldo bloqueado."
          }
        }
      },
      "PixDepositRequest": {
        "type": "object",
        "required": [
          "amount",
          "external_id",
          "clientCallbackUrl",
          "payer"
        ],
        "properties": {
          "amount": {
            "type": "number",
            "format": "double",
            "minimum": 0.01
          },
          "external_id": {
            "type": "string",
            "description": "Referência externa única para a operação."
          },
          "clientCallbackUrl": {
            "type": "string",
            "format": "uri",
            "description": "Endpoint HTTPS público para eventos de callback."
          },
          "payer": {
            "$ref": "#/components/schemas/Payer"
          }
        }
      },
      "PixDepositResponse": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string"
          },
          "qrCodeResponse": {
            "type": "object",
            "properties": {
              "transactionId": {
                "type": "string"
              },
              "status": {
                "type": "string",
                "example": "PENDING"
              },
              "qrcode": {
                "type": "string",
                "description": "Código Pix copia e cola."
              },
              "amount": {
                "type": "string",
                "example": "25.90"
              }
            }
          }
        }
      },
      "CheckoutRequest": {
        "type": "object",
        "required": [
          "amount",
          "external_id",
          "clientCallbackUrl",
          "payer",
          "payment_method"
        ],
        "properties": {
          "amount": {
            "type": "number",
            "format": "double",
            "minimum": 1,
            "maximum": 99999999.99
          },
          "external_id": {
            "type": "string",
            "maxLength": 100
          },
          "clientCallbackUrl": {
            "type": "string",
            "format": "uri"
          },
          "payment_method": {
            "type": "string",
            "enum": [
              "card",
              "boleto"
            ]
          },
          "description": {
            "type": "string"
          },
          "return_url": {
            "type": "string",
            "format": "uri",
            "description": "Opcional; deve usar HTTPS."
          },
          "payer": {
            "type": "object",
            "required": [
              "email"
            ],
            "properties": {
              "name": {
                "type": "string"
              },
              "email": {
                "type": "string",
                "format": "email"
              },
              "document": {
                "type": "string"
              }
            }
          }
        }
      },
      "CheckoutResponse": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string"
          },
          "paymentResponse": {
            "type": "object",
            "properties": {
              "transactionId": {
                "type": "string"
              },
              "status": {
                "type": "string"
              },
              "amount": {
                "type": "string"
              },
              "payment_method": {
                "type": "string",
                "enum": [
                  "card",
                  "boleto"
                ]
              },
              "checkoutUrl": {
                "type": "string",
                "format": "uri"
              },
              "preferenceId": {
                "type": "string"
              }
            }
          }
        }
      },
      "BoletoRequest": {
        "type": "object",
        "required": [
          "amount",
          "external_id",
          "payer"
        ],
        "properties": {
          "amount": {
            "type": "number",
            "format": "double",
            "minimum": 1,
            "maximum": 99999999.99
          },
          "external_id": {
            "type": "string",
            "maxLength": 100
          },
          "description": {
            "type": "string",
            "maxLength": 120
          },
          "payer": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Payer"
              },
              {
                "type": "object",
                "required": [
                  "address"
                ],
                "properties": {
                  "address": {
                    "$ref": "#/components/schemas/BoletoAddress"
                  }
                }
              }
            ]
          }
        }
      },
      "BoletoResponse": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string"
          },
          "paymentResponse": {
            "type": "object",
            "properties": {
              "transactionId": {
                "type": "string"
              },
              "external_id": {
                "type": "string"
              },
              "status": {
                "type": "string"
              },
              "amount": {
                "type": "string"
              },
              "payment_method": {
                "type": "string",
                "example": "boleto"
              },
              "boleto": {
                "type": "object",
                "properties": {
                  "barcode_content": {
                    "type": "string",
                    "nullable": true
                  },
                  "digitable_line": {
                    "type": "string"
                  },
                  "expiration_date": {
                    "type": "string",
                    "nullable": true
                  }
                }
              }
            }
          }
        }
      },
      "WithdrawalRequest": {
        "type": "object",
        "required": [
          "amount",
          "external_id",
          "pix_key",
          "key_type",
          "clientCallbackUrl"
        ],
        "properties": {
          "amount": {
            "type": "number",
            "format": "double"
          },
          "external_id": {
            "type": "string"
          },
          "pix_key": {
            "type": "string"
          },
          "key_type": {
            "type": "string",
            "description": "Tipo da chave Pix aceito para a conta."
          },
          "name": {
            "type": "string"
          },
          "taxId": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "clientCallbackUrl": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "WithdrawalResponse": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string"
          },
          "withdrawal": {
            "type": "object",
            "properties": {
              "transaction_id": {
                "type": "string"
              },
              "status": {
                "type": "string",
                "enum": [
                  "PROCESSING",
                  "COMPLETED",
                  "FAILED"
                ]
              },
              "amount": {
                "type": "number"
              },
              "type": {
                "type": "string",
                "example": "Withdrawal"
              }
            }
          }
        }
      },
      "QrQuoteRequest": {
        "type": "object",
        "required": [
          "qr_code"
        ],
        "properties": {
          "qr_code": {
            "type": "string",
            "maxLength": 4096
          },
          "amount": {
            "type": "number",
            "description": "Necessário quando o QR não informa o valor."
          }
        }
      },
      "QrPaymentRequest": {
        "type": "object",
        "required": [
          "qr_code",
          "external_id",
          "expected_total"
        ],
        "properties": {
          "qr_code": {
            "type": "string",
            "maxLength": 4096
          },
          "external_id": {
            "type": "string",
            "minLength": 16,
            "maxLength": 191,
            "pattern": "^[A-Za-z0-9_-]+$"
          },
          "expected_total": {
            "type": "number",
            "description": "Total da cotação calculada pela rota quote."
          },
          "amount": {
            "type": "number",
            "description": "Necessário quando o QR não informa o valor."
          }
        }
      }
    }
  },
  "paths": {
    "/api/auth/login": {
      "post": {
        "tags": [
          "Autenticação"
        ],
        "summary": "Autenticar credenciais da API",
        "description": "Retorna um token JWT válido por uma hora.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LoginRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Token emitido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LoginResponse"
                }
              }
            }
          },
          "400": {
            "description": "Corpo inválido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Credenciais inválidas.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Conta indisponível para operações de API.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Limite de autenticação atingido."
          }
        }
      }
    },
    "/api/accounts/balance": {
      "get": {
        "tags": [
          "Conta"
        ],
        "summary": "Consultar saldo",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Saldo da conta.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BalanceResponse"
                }
              }
            }
          },
          "401": {
            "description": "Token ausente ou inválido."
          },
          "404": {
            "description": "Carteira não encontrada."
          }
        }
      }
    },
    "/api/payments/deposit": {
      "post": {
        "tags": [
          "Pagamentos"
        ],
        "summary": "Criar cobrança Pix",
        "description": "Retorna o código Pix copia e cola. O valor mínimo é definido para a conta. Use uma referência externa única e confira o extrato antes de repetir após timeout.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PixDepositRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Cobrança criada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PixDepositResponse"
                }
              }
            }
          },
          "400": {
            "description": "Campo ausente ou inválido."
          },
          "401": {
            "description": "Token ausente ou inválido."
          },
          "429": {
            "description": "Limite de criações atingido."
          },
          "500": {
            "description": "Configuração da conta ou serviço indisponível."
          }
        }
      }
    },
    "/api/payments/checkout": {
      "post": {
        "tags": [
          "Pagamentos"
        ],
        "summary": "Criar checkout hospedado",
        "description": "Aceita payment_method card ou boleto. Repetições com a mesma referência e dados recuperam o checkout existente; alterações retornam conflito.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CheckoutRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Checkout criado ou recuperado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckoutResponse"
                }
              }
            }
          },
          "400": {
            "description": "Dados inválidos."
          },
          "401": {
            "description": "Token ausente ou inválido."
          },
          "409": {
            "description": "Referência já usada com dados diferentes."
          },
          "429": {
            "description": "Limite de criações atingido."
          },
          "502": {
            "description": "Checkout hospedado indisponível."
          }
        }
      }
    },
    "/api/payments/boleto": {
      "post": {
        "tags": [
          "Pagamentos"
        ],
        "summary": "Emitir boleto",
        "description": "Emite boleto para adicionar saldo ou cobrar um cliente. O mesmo external_id e valor recuperam a cobrança existente.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BoletoRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Boleto criado ou recuperado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BoletoResponse"
                }
              }
            }
          },
          "400": {
            "description": "Dados do pagador ou valor inválidos."
          },
          "401": {
            "description": "Token ausente ou inválido."
          },
          "409": {
            "description": "Referência reutilizada com valor ou método diferente."
          },
          "429": {
            "description": "Limite de criações atingido."
          },
          "502": {
            "description": "Não foi possível emitir o boleto."
          }
        }
      }
    },
    "/api/payments/boleto/{external_id}/status": {
      "get": {
        "tags": [
          "Pagamentos"
        ],
        "summary": "Consultar e sincronizar status do boleto",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "external_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "maxLength": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Estado e dados atuais do boleto.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BoletoResponse"
                }
              }
            }
          },
          "401": {
            "description": "Token ausente ou inválido."
          },
          "404": {
            "description": "Boleto não encontrado."
          },
          "409": {
            "description": "Dados do pagamento não conferem."
          },
          "429": {
            "description": "Limite de consultas atingido."
          },
          "502": {
            "description": "Não foi possível consultar o boleto."
          }
        }
      }
    },
    "/api/withdrawals/withdraw": {
      "post": {
        "tags": [
          "Saques"
        ],
        "summary": "Enviar Pix para uma chave",
        "description": "Debita o valor e as tarifas configuradas. A conta precisa ter IP autorizado. Uma referência já usada não pode ser repetida.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WithdrawalRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Solicitação processada; o status pode estar PROCESSING.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WithdrawalResponse"
                }
              }
            }
          },
          "400": {
            "description": "Dados inválidos, saldo insuficiente, referência duplicada ou operação recusada."
          },
          "401": {
            "description": "Token ausente ou inválido."
          },
          "403": {
            "description": "IP não autorizado."
          },
          "429": {
            "description": "Limite ou operação anterior em andamento."
          }
        }
      }
    },
    "/api/withdrawals/withdraw/qrcode/quote": {
      "post": {
        "tags": [
          "Saques"
        ],
        "summary": "Calcular pagamento por QR Code Pix",
        "description": "Calcula o valor e as tarifas sem debitar saldo. Se o QR não tiver valor, informe amount.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QrQuoteRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Cotação calculada ou pedido de valor adicional."
          },
          "401": {
            "description": "Token ausente ou inválido."
          },
          "422": {
            "description": "QR Code inválido."
          },
          "503": {
            "description": "Pagamento por QR Code indisponível para a conta."
          }
        }
      }
    },
    "/api/withdrawals/withdraw/qrcode": {
      "post": {
        "tags": [
          "Saques"
        ],
        "summary": "Confirmar pagamento por QR Code Pix",
        "description": "Confirme com o total exato retornado pela cotação. Uma resposta 202 indica que a confirmação ainda está em andamento; confira o extrato antes de tentar novamente.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QrPaymentRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Pagamento concluído."
          },
          "202": {
            "description": "Pagamento em confirmação."
          },
          "400": {
            "description": "Saldo insuficiente ou pagamento recusado."
          },
          "401": {
            "description": "Token ausente ou inválido."
          },
          "403": {
            "description": "IP não autorizado."
          },
          "409": {
            "description": "Referência já pertence a outra operação ou cotação mudou."
          },
          "422": {
            "description": "QR Code ou solicitação inválida."
          },
          "429": {
            "description": "Operação anterior em andamento ou limite atingido."
          },
          "503": {
            "description": "Pagamento por QR Code indisponível."
          }
        }
      }
    }
  }
}
