curl -X POST /api/v1/transfers/42/prepare \
-H "x-api-key: mh_live_abc123..." \
-H "Idempotency-Key: $(uuidgen)"
{
"transfer": { "id": 42, "status": "awaiting_signature" },
"preparedTxs": {
"keeperFundingTx": "AQAAAAAAAA...",
"routeInitTxs": [{ "base64": "AQAAAAAAAA..." }],
"orchestratorInitTx": null,
"sessionInitTxs": ["AQAAAAAAAA...", "AQAAAAAAAA..."],
"dispersionInitTxs": [],
"recentBlockhash": "BpFi8bTNRuLbUnrq7q1N1M...",
"lastValidBlockHeight": 289043200,
"resume": {
"routeAlreadyDeployed": false,
"existingHopCount": 0,
"totalHops": 7,
"orchestratorAlreadyInitialized": true,
"completedStepIndices": [],
"totalSteps": 7,
"keeperAlreadyFunded": false,
"dispersion": null,
"nothingToDo": false
}
}
}
{
"error": {
"code": "MH_035",
"message": "Transfer not in a state that allows this operation — Transfer is completed; there is nothing left to prepare"
}
}
{
"error": {
"code": "MH_040",
"message": "Tier shifted: quote=50bps/1125000flat, on-chain now=50bps/1875000flat (tier 3, usd_micros=163012500)"
}
}
Transfers
Prepare Transactions
Build the unsigned transaction bundle for a transfer
POST
/
api
/
v1
/
transfers
/
{transferId}
/
prepare
curl -X POST /api/v1/transfers/42/prepare \
-H "x-api-key: mh_live_abc123..." \
-H "Idempotency-Key: $(uuidgen)"
{
"transfer": { "id": 42, "status": "awaiting_signature" },
"preparedTxs": {
"keeperFundingTx": "AQAAAAAAAA...",
"routeInitTxs": [{ "base64": "AQAAAAAAAA..." }],
"orchestratorInitTx": null,
"sessionInitTxs": ["AQAAAAAAAA...", "AQAAAAAAAA..."],
"dispersionInitTxs": [],
"recentBlockhash": "BpFi8bTNRuLbUnrq7q1N1M...",
"lastValidBlockHeight": 289043200,
"resume": {
"routeAlreadyDeployed": false,
"existingHopCount": 0,
"totalHops": 7,
"orchestratorAlreadyInitialized": true,
"completedStepIndices": [],
"totalSteps": 7,
"keeperAlreadyFunded": false,
"dispersion": null,
"nothingToDo": false
}
}
}
{
"error": {
"code": "MH_035",
"message": "Transfer not in a state that allows this operation — Transfer is completed; there is nothing left to prepare"
}
}
{
"error": {
"code": "MH_040",
"message": "Tier shifted: quote=50bps/1125000flat, on-chain now=50bps/1875000flat (tier 3, usd_micros=163012500)"
}
}
Probes on-chain state and returns the full bundle of unsigned Solana transactions that
Before building anything,
sourceOwner must sign and broadcast to deploy the transfer.
This endpoint is resumable — re-call it after a partial broadcast to get a fresh blockhash and drop any groups already confirmed on-chain (null fields are already on chain, skip them).
On compliance-enabled networks (mainnet) the bundle includes a flat 0.002 SOL screening fee
transfer that runs at deploy. Ensure the
sourceOwner wallet holds enough SOL to cover
route amount + protocol fees + account rent + keeper funding + the screening fee before
signing — the fee is not part of /estimate. It pays for the screen and is not refunded,
whether the route is clean or flagged. See Compliance & screening./prepare re-checks the quoted fee tier against the pool price the program
will read. If the price has crossed a tier boundary since the quote, it returns MH_040: create a
new transfer to re-quote. See Pricing & revenue share.
A transfer that is already completed, refunded or expired has nothing left to prepare:
/prepare returns MH_035 (409) and no transactions, so a finished transfer can never be funded
twice.
Path parameters
integer
required
The internal transfer ID.
Response
object
Current transfer state.
object
Bundle of unsigned transactions to sign and broadcast.
Show preparedTxs fields
Show preparedTxs fields
string | null
Broadcast FIRST. Base64 VersionedTransaction that transfers SOL to the assigned keeper.
null if keeper is already funded.
If your client broadcast it but crashed before confirm-broadcast, just call /prepare again:
the landed funding is found on chain and recorded, and you are not asked to pay it twice.array
Array of
{ base64 } VersionedTransactions to initialize the on-chain route. Empty if route is already deployed.string | null
Base64 legacy Transaction to initialize the orchestrator config PDA.
null if already initialized.array
Array of base64 VersionedTransactions to initialize step state PDAs. Entries for already-initialized steps are omitted.
array
Multi-destination transfers only (always
[] otherwise). Base64
VersionedTransactions that set up the payout orchestrator, one committed step per destination,
and the route’s exit binding. Signed by sourceOwner only. Broadcast after routeInitTxs,
in order. Pieces already on chain are omitted.string
Blockhash used to build these transactions. Sign and broadcast before this expires (~60s).
integer
Last block height at which these transactions are valid.
object
Snapshot of on-chain state used to build the bundle.
curl -X POST /api/v1/transfers/42/prepare \
-H "x-api-key: mh_live_abc123..." \
-H "Idempotency-Key: $(uuidgen)"
{
"transfer": { "id": 42, "status": "awaiting_signature" },
"preparedTxs": {
"keeperFundingTx": "AQAAAAAAAA...",
"routeInitTxs": [{ "base64": "AQAAAAAAAA..." }],
"orchestratorInitTx": null,
"sessionInitTxs": ["AQAAAAAAAA...", "AQAAAAAAAA..."],
"dispersionInitTxs": [],
"recentBlockhash": "BpFi8bTNRuLbUnrq7q1N1M...",
"lastValidBlockHeight": 289043200,
"resume": {
"routeAlreadyDeployed": false,
"existingHopCount": 0,
"totalHops": 7,
"orchestratorAlreadyInitialized": true,
"completedStepIndices": [],
"totalSteps": 7,
"keeperAlreadyFunded": false,
"dispersion": null,
"nothingToDo": false
}
}
}
{
"error": {
"code": "MH_035",
"message": "Transfer not in a state that allows this operation — Transfer is completed; there is nothing left to prepare"
}
}
{
"error": {
"code": "MH_040",
"message": "Tier shifted: quote=50bps/1125000flat, on-chain now=50bps/1875000flat (tier 3, usd_micros=163012500)"
}
}

