Create transfer batch
Executes an outbound transfer batch from one exact SDP Wallet ID, chunks recipients into Solana transactions, and returns the batch, recipient, and transfer records.
POST/Run in dashboard
Requires an Idempotency-Key: a retry with the same key and request returns the original batch without another on-chain submission. Recipient order does not change the request. A native SOL batch is refused with 409 AMOUNT_BELOW_RENT_MINIMUM, before anything is written or sent, when a recipient whose address has no account gets less than the rent-exempt minimum (details.recipientIndexes) or the source wallet would be left holding more than nothing but less than the minimum (details.sourceRemainder). A token batch is refused with 409 SPONSORED_RENT_OVER_CAP when one recipient's new token account costs more rent than the sponsor funds in a transaction (details.recipientIndex). A recipient's counterparty account archived while the batch is being prepared returns 404, and one whose address changed returns 409 TRANSFER_BATCH_DESTINATION_CHANGED (details.counterpartyAccountIds); either way nothing is written or sent, so the same request can be sent again. A chunk the source wallet cannot fund fails its recipients and transfer with the reason in error (the wallet does not have enough funds); the batch still returns 200. An externalId already used by a batch on the same project and source wallet never pays again: the same request from the same credential returns that batch, and any other returns 409 TRANSFER_BATCH_RUN_EXISTS with its batchId in error.details. To finish a batch after a partial failure, call POST /v1/payments/transfer-batches/{batchId}/retry: it sends only the batch's failed recipients. Never resend the whole batch under a new Idempotency-Key, which pays the recipients that succeeded again.
Requires an API key with the transfer_batches.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 a custody-executed outbound transfer batch.
-
projectId·string· min length 1 · exampleprj_exampleProject identifier for the transfer batch context.
-
sourceCustodyWalletId·string· required · min length 1 · examplecwlt_exampleExact SDP Wallet ID (
idfromGET /v1/wallets). -
token·string· required · exampleSOLToken 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. -
recipients·object[]· required · min items 1 · max items 500Counterparty-account recipients to pay in this batch.
-
externalId·string· min length 1 · max length 256 -
counterpartyId·string· min length 1 -
counterpartyAccountId·string· required · min length 1 -
amount·string· required
-
-
externalId·string· required · min length 1 · max length 256 · examplepayroll_2026_06_30Your ID for this payout run, unique per project and source wallet. Derive it from your own data so a restarted worker computes the same value (for example
payroll-2026-05); never generate it at random, which gives no protection. Sending the same request again with thisexternalId, from the same credential, under any Idempotency-Key returns the existing batch; a different request with it returns409 TRANSFER_BATCH_RUN_EXISTSwith the existing batch'sbatchIdinerror.details. Neither pays anyone again. New recipients go in a new run with a newexternalId. Required: it is what stops a resend under a new Idempotency-Key from paying the same recipients twice.
Responses
-
200Transfer batch created-
data·object· requiredTransfer batch response payload.
-
batch·object· requiredTransfer batch details.
-
id·string· required · examplexbatch_exampleTransfer batch identifier.
-
organizationId·string· required · min length 1 · exampleorg_exampleOrganization identifier.
-
projectId·string· required · min length 1 · exampleprj_exampleProject identifier for the transfer batch.
-
externalId·string | null· required · examplepayroll_2026_06_30Caller-provided batch correlation ID.
-
sourceCustodyWalletId·string | null· required · examplecwlt_exampleExact source SDP Wallet ID, or null for unresolved legacy batches.
-
sourceProviderWalletId·string· required · min length 1 · exampleprivy_wallet_123Source Provider wallet ID retained as evidence.
-
sourceAddress·string· required · min length 32 · max length 44 · exampleSo11111111111111111111111111111111111111112Source custody wallet address.
-
token·string· required · exampleSOLToken symbol or mint address.
-
status·string· required · one ofpending,processing,confirmed,failed,partially_failed,archived· exampleprocessingTransfer batch status.
-
totalAmount·string | null· required · example1000.00Total requested batch amount in UI units, when computed.
-
recipientCount·integer· required · example20Number of recipient rows in the batch.
-
transactionCount·integer· required · example1Number of Solana transaction chunks for the batch.
-
createdAt·string· required · formatdate-time· example2025-01-01T00:00:00.000ZISO 8601 timestamp.
-
updatedAt·string· required · formatdate-time· example2025-01-01T00:00:00.000ZISO 8601 timestamp.
-
-
recipients·object[]Recipient rows for the batch, included on detail responses.
-
id·string· requiredTransfer batch recipient identifier.
-
batchId·string· requiredParent transfer batch identifier.
-
transferId·string | null· required · min length 1 · examplexfr_examplePayment transfer chunk that settled this recipient, once assigned.
-
externalId·string | null· required · examplepayroll_row_001Caller-provided recipient correlation ID.
-
counterpartyId·string· required · examplecpty_exampleCounterparty identifier for the recipient.
-
counterpartyAccountId·string· required · examplecpacct_exampleCounterparty account identifier for the recipient.
-
destination·string· required · min length 32 · max length 44 · exampleSo11111111111111111111111111111111111111112Resolved Solana destination address.
-
amount·string· required · example100.00Token amount in UI units (decimal string).
-
status·string· required · one ofpending,processing,confirmed,failed,archived· examplependingTransfer batch recipient status.
-
error·string | null· requiredRecipient-level failure reason, when available. A recipient the source wallet could not fund reads "The wallet used for this payment does not have enough funds. Add funds and try again."
-
createdAt·string· required · formatdate-time· example2025-01-01T00:00:00.000ZISO 8601 timestamp.
-
updatedAt·string· required · formatdate-time· example2025-01-01T00:00:00.000ZISO 8601 timestamp.
-
-
transfers·object[]Payment transfer chunk records associated with this batch.
-
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 thetransfer_batches.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.