curl -X POST /api/v1/transfers/42/confirm-broadcast \
-H "x-api-key: mh_live_abc123..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"routeInitSignatures": [],
"keeperFundingSignature": "7hGpRt4bJm2Kx9Nv5aZqYe3uLk8dQ1P..."
}'
curl -X POST /api/v1/transfers/42/confirm-broadcast \
-H "x-api-key: mh_live_abc123..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"routeInitSignatures": ["4mGxFn7mN1KqQe6a..."],
"orchestratorInitSignature": "2qYzLf1xJx4oRz9M...",
"sessionInitSignatures": ["9rTpWx2aKm3Nv8Qz..."],
"keeperFundingSignature": "7hGpRt4bJm2Kx9Nv..."
}'
{
"transfer": {
"id": 42,
"status": "processing"
}
}
{
"error": {
"code": "MH_039",
"message": "keeperFundingSignature is required"
}
}
{
"error": {
"code": "MH_041",
"message": "Destinations not yet bound on chain: broadcast preparedTxs.dispersionInitTxs, then confirm again: orchestrator=true steps=2/2 routeExit=false"
}
}
{
"error": {
"code": "MH_035",
"message": "Transfer not in a state that allows this operation"
}
}
Transfers
Confirm Broadcast
Report Solana transaction signatures after broadcasting prepared transactions
POST
/
api
/
v1
/
transfers
/
{transferId}
/
confirm-broadcast
curl -X POST /api/v1/transfers/42/confirm-broadcast \
-H "x-api-key: mh_live_abc123..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"routeInitSignatures": [],
"keeperFundingSignature": "7hGpRt4bJm2Kx9Nv5aZqYe3uLk8dQ1P..."
}'
curl -X POST /api/v1/transfers/42/confirm-broadcast \
-H "x-api-key: mh_live_abc123..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"routeInitSignatures": ["4mGxFn7mN1KqQe6a..."],
"orchestratorInitSignature": "2qYzLf1xJx4oRz9M...",
"sessionInitSignatures": ["9rTpWx2aKm3Nv8Qz..."],
"keeperFundingSignature": "7hGpRt4bJm2Kx9Nv..."
}'
{
"transfer": {
"id": 42,
"status": "processing"
}
}
{
"error": {
"code": "MH_039",
"message": "keeperFundingSignature is required"
}
}
{
"error": {
"code": "MH_041",
"message": "Destinations not yet bound on chain: broadcast preparedTxs.dispersionInitTxs, then confirm again: orchestrator=true steps=2/2 routeExit=false"
}
}
{
"error": {
"code": "MH_035",
"message": "Transfer not in a state that allows this operation"
}
}
After signing and broadcasting the prepared transactions to Solana, call this endpoint to record the signatures. Once the full bundle is recorded the transfer advances to
processing and the keeper network takes over.
This endpoint is resumable and called twice in the normal flow:
- Immediately after
keeperFundingTx— call with justkeeperFundingSignatureand an emptyrouteInitSignatures: []. This records the funding right away. (If your client crashes before this call, nothing is lost: the next/prepareorconfirm-broadcastfinds the landed funding on chain and never asks for a second one.) - After all remaining transactions — call with
routeInitSignatures,orchestratorInitSignature,sessionInitSignaturesand, for a multi-destination transfer,dispersionInitSignatures.
Only make the final call once every transaction has actually confirmed on-chain. This
endpoint advances the transfer only if it can verify that the route, the orchestrator config,
and all step PDAs already exist on-chain. If you report signatures for transactions that
haven’t landed yet (e.g. you broadcast lazily or out of order, or a tx silently dropped), the
transfer stays at its pre-broadcast state, the keeper never picks it up, and the route stalls
with no error. Broadcast in order —
keeperFundingTx → routeInitTxs → orchestratorInitTx
→ sessionInitTxs → dispersionInitTxs — wait for confirmations, then call this endpoint. If a transfer appears
stuck, re-call /prepare to see which groups are still
missing (non-null fields), rebroadcast them, then confirm again.Path parameters
integer
required
The internal transfer ID.
Request body
array
required
Base58 signatures for each
routeInitTxs entry. Pass an empty array [] on the intermediate call (after keeper funding only).string
Base58 signature for
keeperFundingTx. Required whenever /prepare emitted a keeperFundingTx, unless that funding already landed on chain (it is then found and recorded for you). Omitting it when no funding landed returns MH_039.string
Base58 signature for
orchestratorInitTx. Omit when that field was null.array
Base58 signatures for each
sessionInitTxs entry. Omit when empty.array
Multi-destination transfers: base58 signatures for each
dispersionInitTxs entry, in order.For a multi-destination transfer this endpoint checks the chain, not the signatures you send. It
won’t start the transfer until the payout orchestrator, every destination step and the route’s
exit binding exist on chain. Until then it returns
MH_041 and changes nothing. Broadcast
whatever /prepare still returns, then call again.Response
object
Updated transfer object.
status becomes processing once the full bundle is recorded.curl -X POST /api/v1/transfers/42/confirm-broadcast \
-H "x-api-key: mh_live_abc123..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"routeInitSignatures": [],
"keeperFundingSignature": "7hGpRt4bJm2Kx9Nv5aZqYe3uLk8dQ1P..."
}'
curl -X POST /api/v1/transfers/42/confirm-broadcast \
-H "x-api-key: mh_live_abc123..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"routeInitSignatures": ["4mGxFn7mN1KqQe6a..."],
"orchestratorInitSignature": "2qYzLf1xJx4oRz9M...",
"sessionInitSignatures": ["9rTpWx2aKm3Nv8Qz..."],
"keeperFundingSignature": "7hGpRt4bJm2Kx9Nv..."
}'
{
"transfer": {
"id": 42,
"status": "processing"
}
}
{
"error": {
"code": "MH_039",
"message": "keeperFundingSignature is required"
}
}
{
"error": {
"code": "MH_041",
"message": "Destinations not yet bound on chain: broadcast preparedTxs.dispersionInitTxs, then confirm again: orchestrator=true steps=2/2 routeExit=false"
}
}
{
"error": {
"code": "MH_035",
"message": "Transfer not in a state that allows this operation"
}
}

