Skip to main content
Proposals are on-chain treasury payout requests. A proposal moves funds from a vault’s covenant UTXO to a recipient address after 2-of-3 signers approve it.

List All Proposals

GET /api/proposals?status=pending
Returns all proposals across all vaults, optionally filtered by status (pending, approved, executed, rejected).

List Vault Proposals

GET /api/vaults/:vaultId/proposals
Returns all proposals scoped to a specific vault.

Get Proposal

GET /api/proposals/:id
Returns a single proposal with approval list, amounts, and status.

Create Proposal (Off-chain)

POST /api/vaults/:vaultId/proposals
x-user-address: bchtest:q...

{
  "recipient": "bchtest:qs...",
  "amount": 2.5,
  "reason": "Quarterly contractor payout"
}
Creates a pending proposal record in the database. No on-chain transaction yet.

Create On-chain Proposal

POST /api/proposals/:id/create-onchain
x-user-address: bchtest:q...
Deploys the ProposalCovenant on-chain with the payout parameters, returns a wcTransaction for the proposer to sign and fund. Response
{
  "success": true,
  "proposalContractAddress": "bchtest:p...",
  "tokenCategory": "hex32",
  "fundingSatoshis": "250000000",
  "wcTransaction": { ... }
}

Confirm Proposal Creation

POST /api/proposals/:id/confirm-create

{ "txHash": "hex64" }
Verifies the covenant state NFT output exists on-chain and transitions the proposal to pending.

Approve On-chain

POST /api/proposals/:id/approve-onchain
x-user-address: bchtest:q...
Builds the approval wcTransaction for one of the 3 signers. Each signer must call this and sign independently. Response
{
  "success": true,
  "proposalContractAddress": "bchtest:p...",
  "newApprovalCount": 1,
  "isApproved": false,
  "wcTransaction": { ... }
}

Confirm Approval

POST /api/proposals/:id/confirm-approval
x-user-address: bchtest:q...

{ "txHash": "hex64" }
Records the signer’s approval. When approvalCount reaches 2, the proposal status becomes approved.

Execute Payout

POST /api/proposals/:id/execute
x-user-address: bchtest:q...
Vault signer initiates the 2-of-2 payout signing session. Returns a wcTransaction with "broadcast": false — the wallet must sign but not broadcast. Response
{
  "success": true,
  "sessionId": "...",
  "wcTransaction": { ..., "broadcast": false },
  "signaturesCollected": 0,
  "requiredSignatures": 2,
  "requiredSignerAddresses": ["bchtest:q...", "bchtest:q..."],
  "payoutSatoshis": "250000000"
}

Submit Execute Signature

POST /api/proposals/:id/execute-signature
x-user-address: bchtest:q...

{
  "sessionId": "...",
  "signedTransaction": "0200..."
}
Each of the 2 required signers submits their signature. When the second signature arrives, the backend broadcasts the transaction automatically and marks the proposal executed.

Broadcast

POST /api/proposals/broadcast

{
  "txHex": "0200...",
  "txType": "execute",
  "vaultId": "...",
  "proposalId": "..."
}
Low-level broadcast endpoint. Used when automatic broadcast is not desired.