Execute transfer (custody)
Executes an on-chain transfer using the exact SDP Wallet ID (id from /v1/wallets) and server-side custody signing.
POST/Run in dashboard
Requires an Idempotency-Key: a retry with the same key and request returns the original transfer instead of sending a second one. A 200 may return a processing transfer with its signature when broadcast or confirmation is still being reconciled; do not create a replacement transfer for that payment. A native SOL transfer is refused with 409 AMOUNT_BELOW_RENT_MINIMUM, before anything is written or sent, when it pays less than the rent-exempt minimum to an address with no account or would leave the source wallet holding more than nothing but less than the minimum. When the source wallet's policy at its custody provider refuses to sign, the transfer fails with 422 CUSTODY_POLICY_REFUSED and nothing is signed or sent; the message names the provider whose policy to review. When the source wallet holds less SOL, or less of the token in its token account, than the amount, the transfer fails with 400 TRANSACTION_FAILED and is recorded failed with that reason in error; a retry with the same Idempotency-Key returns the same error, so fund the wallet and send again with a new key. A source wallet with no token account for the token is refused earlier with 400 BAD_REQUEST.
Requires an API key with the transfers.execute action.
Headers
-
x-project-id·string· min length 1 · exampleprj_exampleRequired for dashboard sessions. API keys use their assigned project.
-
Idempotency-Key·string· requiredUse a unique 1–255 printable ASCII key per operation. Reuse it with the same request on retries. An in-flight attempt returns
409; a changed request returns422. Retry and recovery rules.
Request body
application/json · required
Create transfer request payload for a custody-managed source wallet. This endpoint does not provision wallets.
-
projectId·string· min length 1 · exampleprj_exampleProject identifier for the transfer context.
-
sourceCustodyWalletId·string· required · min length 1 · examplecwlt_exampleExact SDP Wallet ID (
idfromGET /v1/wallets). -
destination·string· required · example7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsUDestination wallet address.
-
counterpartyAccountId·string· min length 1 · examplecpacct_exampleThe counterparty's active Solana
crypto_walletaccount thatdestinationbelongs to, as a transfer batch recipient names it. The transfer record is tied to that counterparty. Its address must equaldestination. -
token·string· required · exampleEPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1vToken to transfer. Pass
SOLfor the native token, a well-known token symbol (e.g.USDC) resolved to the configured cluster's mint, or a base58 SPL mint address. Custom tokens must be specified by their on-chain mint. -
amount·string· required · example100.00Token amount in UI units (decimal string).
-
memo·string· max length 256Optional memo for the transfer.
Responses
-
200Transfer executed-
data·object· requiredTransfer response payload.
-
transfer·object· requiredTransfer details.
-
fireblocks·objectNative Fireblocks transfer tracking. A processing payment may await approval in Fireblocks; network fees are paid by its source vault. A missing transaction ID means submission is still being reconciled. Do not create a replacement payment while its outcome is unknown.
-
transactionId·string | null· required -
status·string· required -
subStatus·string | null· required
-
-
id·string· required · min length 1 · examplexfr_exampleTransfer identifier (SDP record ID, not the on-chain signature).
-
organizationId·string· required · min length 1 · exampleorg_exampleOrganization identifier.
-
custodyWalletId·string | null· required · examplecwlt_exampleExact SDP Wallet ID associated with the persisted transfer, or null for unresolved legacy and observed rows.
-
providerWalletId·string· required · min length 1 · exampleprivy_wallet_123Provider wallet ID retained as evidence; not an execution selector.
-
projectId·string· min length 1 · exampleprj_exampleProject identifier for the transfer.
-
type·string· required · exampletransferTransfer type.
-
kind·string· requiredDashboard transaction kind derived from the transfer type and originating Payments resource.
-
direction·string· required · one ofinbound,outbound· exampleoutboundTransfer direction.
-
status·string· required · exampleconfirmedTransfer status.
-
signature·string | null· required · examplesig_exampleSolana transaction signature (tx id/hash).
-
serializedTx·string | null· required · min length 1 · examplebase64_tx_exampleBase64-encoded transaction payload, if available. Always null for transfer batch chunks, whose signed bytes stay server-side.
-
slot·integer | null· required · example123456Slot number, if confirmed.
-
blockTime·string | null· required · formatdate-time· example2025-01-01T00:00:00.000ZBlock time, if confirmed.
-
confirmedAt·string· formatdate-time· example2025-01-01T00:00:00.000ZWhen SDP confirmed the transfer on chain.
-
fee·integer | null· required · example0Network fee the source wallet paid, in lamports: 0 when SDP sponsored the transaction, null when not known.
-
error·string | null· required · exampleSignature failedError message if the transaction failed.
-
initiatedBy·objectInitiator that triggered the transfer.
-
type·string· required · one ofapi_key,user,system· exampleapi_keyInitiator type.
-
id·stringInitiator identifier if applicable.
-
display·stringHuman-friendly label for the initiator.
-
-
source·string· min length 32 · max length 44 · exampleSo11111111111111111111111111111111111111112Source wallet address.
-
destination·string· min length 32 · max length 44 · exampleSo11111111111111111111111111111111111111112Destination wallet address.
-
memo·string· max length 256Optional memo for the transfer.
-
token·stringToken symbol or mint address.
-
amount·string· example100.00Token amount in UI units (decimal string).
-
counterpartyId·string· examplecpty_exampleCounterparty tied to the transfer record, when available.
-
counterpartyDisplayName·string· exampleAcme StudioCurrent display name of the counterparty tied to the transfer.
-
risk·objectOptional risk evaluation for the transfer.
-
provider·string· required · exampletrmRisk scoring provider.
-
score·string· required · example0.12Provider-specific risk score.
-
level·string· required · one oflow,medium,high,unknown· examplelowRisk level classification.
-
evaluatedAt·string· required · formatdate-time· example2025-01-01T00:00:00.000ZTimestamp when risk was evaluated.
-
-
createdAt·string· required · formatdate-time· example2025-01-01T00:00:00.000ZCreation timestamp.
-
updatedAt·string· required · formatdate-time· example2025-01-02T00:00:00.000ZLast update timestamp.
-
-
-
meta·object· requiredResponse metadata.
-
requestId·string· required · min length 1 · examplereq_exampleRequest identifier for tracing.
-
timestamp·string· required · formatdate-time· example2025-01-01T00:00:00.000ZISO 8601 timestamp.
-
-
-
400Bad Request-
error·object· requiredStandard error payload.
-
code·string· requiredMachine-readable error code.
-
message·string· requiredHuman-readable error message.
-
details·objectOptional error details for debugging.
*·any
-
-
meta·object-
requestId·string· min length 1 · examplereq_exampleRequest identifier for tracing.
-
-
-
401UnauthorizedSame fields as
400. -
403Forbidden. Requires thetransfers.executeaction: an API key must hold it (a module'smanageorexecutealso covers itsread), and a signed-in member's organization role must include it. Without it the error is INSUFFICIENT_PERMISSIONS anderror.details.actionnames the action. Any other 403 names its own reason.Same fields as
400. -
404Not FoundSame fields as
400. -
409ConflictSame fields as
400. -
422Unprocessable EntitySame fields as
400. -
500Internal Server ErrorSame fields as
400. -
502Bad GatewaySame fields as
400. -
503Service UnavailableSame fields as
400.