Navigate...
Distributions
Create and manage token distributions. The Distributions API handles sending tokens to multiple recipients in batched transactions, with full status tracking and cancellation support.
Overview
A distribution is the core operation of Multisender — sending tokens from one wallet to many recipients. There are two flows:Single-step flow (recommended for SDK): Call distribute() to create a distribution and receive transaction calldata in one call. Then sign and submit the transactions to the blockchain.Two-step flow (for review before execution): Create a draft with createDraft(), optionally edit with updateDraft() / replaceRecipients(), then call prepare() to generate calldata.Important: Multisender generates calldata but does not send transactions. You must sign and submit each transaction using your wallet or web3 library.
You must submit transactions yourself
Multisender does not send transactions on your behalf. distribute() returns calldata — pre-built transaction objects (to, data, value, gasLimit). You must sign and submit each transaction to the blockchain using your wallet or web3 library. The platform then tracks their on-chain status.
Recipients format
For distribute(): provide recipients as a CSV string ("address,amount\naddress,amount") or as an array of [address, amount] tuples. For createDraft(): provide as RecipientDto[] objects { address, amount } or reference a list by listId.
Key Concepts
Single-step flow
Call distribute() to create + prepare a distribution and receive calldata in one response. Returns DistributeResult with { distribution, calldata }. Recommended for programmatic SDK usage.
Two-step flow
Create a DRAFT with createDraft(), review and edit recipients with replaceRecipients(), then call prepare() to transition to PREPARED. Use this when you need human review before execution.
Distribution status
DRAFT — created, editable. PREPARED — calldata generated, ready for on-chain execution. IN_PROGRESS — transactions submitted. COMPLETED — all batches confirmed. PARTIALLY_COMPLETED — some failed, some confirmed. FAILED — all batches failed. CANCELLED — manually cancelled.
Transaction batch
Large distributions are split into multiple on-chain transactions (batches). Each batch has its own status (PENDING → SUBMITTED → CONFIRMED or FAILED), transaction hash, and recipient count. Monitor batches with getStats() and getTransactions().
Calldata
Pre-built transaction objects returned by distribute(). Each contains to (contract address), data (encoded function call), value (native token amount), and gasLimit. You sign and submit these — Multisender does not send them for you.
Token approval
ERC-20 tokens require an on-chain approve() transaction before distributing. Use getApproveCalldata() to generate the approval transaction. Native tokens (ETH, POL, etc.) do not require approval — pass 0x0000000000000000000000000000000000000000 as tokenAddress to target the native currency.
Idempotency key
A unique string you provide to prevent duplicate distributions when retrying. If you send the same idempotencyKey twice, the second request returns the existing distribution instead of creating a new one.
Quick Start
Common Workflows
Single-step distribution (recommended)
Create a distribution and get calldata in one call. Best for programmatic SDK usage when you want to send tokens immediately.
1
getApproveCalldata()
Generate ERC-20 approval transaction and submit it on-chain (skip for native tokens like ETH)
2
distribute()
Create the distribution and receive `{ distribution, calldata }` — the calldata contains transactions to sign
3
Sign & submit transactions
Sign and submit each transaction from `calldata.transactions` using your wallet (ethers.js, viem, web3.js)
4
getStats()
Poll for batch completion — when `confirmed + failed === total`, the distribution is done
Two-step distribution (with review)
Create a draft, review recipients, then prepare and execute. Best when you need human review before committing.
1
createDraft()
Create a distribution in DRAFT status with recipients or a list ID
2
updateDraft() / replaceRecipients()
Optionally update metadata or replace recipients while in DRAFT
3
getApproveCalldataForDistribution()
Generate approval for the exact amount needed by this distribution (ERC-20 only)
4
prepare()
Generate calldata — transitions from DRAFT to PREPARED
5
getTransactions()
Retrieve the generated transaction batches with calldata for signing
Methods
sdk.distributions.list(params?)
Retrieve all distributions for your project with pagination. Use this to browse distribution history, monitor active distributions, or find a specific distribution by name. Results are sorted by creation date by default.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
params.page | number | No | Page number (default: 1) |
params.limit | number | No | Items per page (default: 10) |
params.search | string | No | Filter distributions by name |
params.orderBy | string | No | Field to sort by (e.g. createdAt) |
params.orderDir | 'ASC' | 'DESC' | No | Sort direction ASC DESC |
Returns
PaginatedResponse<Distribution>
Paginated list of distribution objects
Errors
The request is not authorized
You do not have permission to access this resource
See Also
get()
Get full details for a specific distribution by ID
distribute()
Create a new distribution
sdk.distributions.get(id)
Retrieve a single distribution by ID. Returns full details including current status, chain/token info, recipient and amount totals, and timestamps. Use this to check the state of a distribution or to poll for status changes after execution.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Distribution ID |
Returns
Distribution
Full distribution object
Errors
Invalid distribution id
The request is not authorized
You do not have permission to access this resource
Distribution not found
Notes
Status lifecycle: DRAFT → PREPARED → IN_PROGRESS → COMPLETED / FAILED / CANCELLED
executedAt is set when execution starts; completedAt is set when all batches finish
See Also
getStats()
Get batch-level completion counts for progress tracking
getTransactions()
View individual transaction batches and their on-chain hashes
cancel()
Cancel this distribution if not yet completed
sdk.distributions.getStats(id)
Get aggregate transaction batch statistics for a distribution. Returns counts of batches by status — use this to build progress bars or determine when a distribution is complete. A distribution is finished when confirmed + failed === total.
Poll getStats() periodically to show a progress bar. The distribution is done when confirmed + failed === total. Check failed > 0 to detect partial failures.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Distribution ID |
Returns
DistributionStats
Batch status counts
Errors
Invalid distribution id
The request is not authorized
You do not have permission to access this resource
Distribution not found
See Also
get()
Get the overall distribution status and details
getTransactions()
Inspect individual batches — get txHash for block explorer verification
sdk.distributions.getTransactions(id, params?)
Retrieve individual transaction batches for a distribution with pagination. Each batch represents a group of recipients processed in a single on-chain transaction. Use this to get transaction hashes for block explorer verification or to debug failed batches.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Distribution ID |
params.page | number | No | Page number (default: 1) |
params.limit | number | No | Items per page (default: 10) |
params.orderBy | string | No | Field to sort by |
params.orderDir | 'ASC' | 'DESC' | No | Sort direction ASC DESC |
Returns
PaginatedResponse<DistributionTransaction>
Paginated list of transaction batches
Errors
Invalid distribution id
The request is not authorized
You do not have permission to access this resource
Distribution not found
Notes
Transaction batch statuses: PENDING → SUBMITTED → CONFIRMED (or FAILED)
txHash is available after submission — use it to verify on a block explorer
Each batch covers a subset of recipients; recipientCount shows how many
See Also
getStats()
Get aggregate counts instead of individual batches
get()
Get the overall distribution status
sdk.distributions.getApproveCalldata(request)
Generate ERC-20 token approval calldata. Returns a transaction that calls approve(spender, amount) on the token contract, allowing the Multisender contract to spend your tokens. Sign and submit this transaction before distributing ERC-20 tokens. Skip this method entirely for native currency (ETH, POL, BNB, etc.) — native tokens do not require approval. Calling it with the zero address produces an invalid approval.
Skip for native currency
This method is only needed for ERC-20 token distributions. If you are distributing native currency (ETH, POL, BNB, etc.) — i.e. using tokenAddress: 0x0000000000000000000000000000000000000000 in distribute() / createDraft() — skip this step entirely. No approval is required for native tokens.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
request.chainId | number | Yes | Blockchain chain ID (determines which Multisender contract is the spender) |
request.tokenAddress | string | Yes | ERC-20 token contract address to approve. Do NOT pass the zero address here — native currency (ETH, POL, BNB, etc.) does not require approval, skip this method entirely. |
request.amount | string | Yes | Amount in wei (integer string, no decimals) or "max" for unlimited approval (uses maxUint256) |
Returns
ApproveCalldata
Pre-built approval transaction — sign and submit to your wallet
Errors
Validation error
The request is not authorized
You do not have permission to access this resource
Notes
Only needed for ERC-20 tokens. Native currency (ETH, POL, BNB, etc.) does not require approval — skip this method entirely
Do not call this method with the zero address 0x0000000000000000000000000000000000000000 — it represents native currency, for which no approval exists
amount: "max" sets unlimited approval — convenient for repeated distributions
The spender in the response is the Multisender contract address for the specified chain
See Also
distribute()
Next step — create distribution after approval
getApproveCalldataForDistribution()
Auto-calculate exact approval amount from an existing distribution
getChain()
Find the Multisender contract address for a chain
sdk.distributions.getApproveCalldataForDistribution(id)
Generate ERC-20 approval calldata for a specific distribution. Automatically calculates the exact approval amount based on the distribution's totalAmount — no need to compute it yourself. Use this in the two-step flow after creating a draft.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Distribution ID (must have a non-zero totalAmount) |
Returns
ApproveCalldata
Pre-built approval transaction with exact amount from the distribution
Errors
Invalid distribution id
The request is not authorized
You do not have permission to access this resource
Distribution not found
Notes
The distribution must have recipients (non-zero totalAmount) for this to work
Returns the same ApproveCalldata format as getApproveCalldata() but calculates amount automatically
Skip this method if the distribution targets native currency (tokenAddress = 0x0000000000000000000000000000000000000000) — native transfers do not require approval
See Also
getApproveCalldata()
Manual approval with custom amount (for single-step flow)
prepare()
Next step — prepare calldata after token approval
createDraft()
Create the draft distribution first
sdk.distributions.distribute(request)
Create a distribution and return both the distribution object and transaction calldata in a single call. Accepts inline recipients (csv or recipients tuples) — does not support listId. To distribute from an existing list, use createDraft({ listId }) → prepare(). This is the primary method for SDK integrations — you get everything needed to sign and submit transactions immediately.
Sending native currency (ETH, POL, BNB, …)
Pass the zero address 0x0000000000000000000000000000000000000000 as tokenAddress to distribute native currency. No approve() step is required, and the generated calldata already includes the required value on each transaction. The server normalizes tokenSymbol to the chain’s nativeSymbol.
Token approval required for ERC-20
ERC-20 tokens require an on-chain approve() before distributing. Generate and submit approval calldata first.
Get approval calldata
Calldata summary
The calldata.summary field provides aggregate info: totalRecipients, totalAmount, estimatedGas, and estimatedCost. Use this to show the user a confirmation before signing.
Need to use an existing list?
distribute() only accepts inline recipients (csv or recipients tuples). To distribute from an existing recipient list, use the two-step flow: createDraft({ listId }) → prepare().
Use createDraft() with listId
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
request.chainId | number | Yes | Target blockchain chain ID (1=Ethereum, 137=Polygon, 8453=Base, 42161=Arbitrum, 10=Optimism) |
request.tokenAddress | string | Yes | ERC-20 token contract address to distribute. For native currency (ETH, POL, BNB, etc.) pass the zero address 0x0000000000000000000000000000000000000000. |
request.tokenSymbol | string | Yes | Token symbol (e.g. "USDC", "WETH"). For native currency the server normalizes this to the chain’s nativeSymbol — you can pass any placeholder. |
request.recipients | string[][] | No | Array of [address, amount] tuples — mutually exclusive with csv |
request.csv | string | No | CSV string with address,amount per line — mutually exclusive with recipients |
request.account | string | Yes | Sender wallet address — required, calldata is generated for this sender |
request.idempotencyKey | string | No | Unique key to prevent duplicate distributions on retry |
Returns
DistributeResult
{ distribution, calldata } — distribution object + transactions to sign and submit
Errors
Validation error
The request is not authorized
You do not have permission to access this resource
Notes
This is the recommended method for SDK integrations — returns everything in one call
The calldata.summary provides totals across all transactions for display purposes
Provide either recipients or csv, not both — listId is not supported
Use idempotencyKey to safely retry failed requests without creating duplicates
ERC-20 tokens require a prior approve() to the Multisender contract
See Also
getApproveCalldata()
Generate ERC-20 approval before distributing (required step for ERC-20)
getStats()
Monitor batch completion after submitting transactions
createDraft()
Use an existing recipient list via listId (not supported in distribute)
sdk.distributions.createDraft(request)
Create a distribution in DRAFT status. This is the first step of the two-step flow — create a draft, optionally review and edit recipients, then call prepare() to generate calldata. You can provide recipients directly, reference an existing list by listId, or add recipients later with replaceRecipients().
Sending native currency (ETH, POL, BNB, …)
Pass the zero address 0x0000000000000000000000000000000000000000 as tokenAddress to target the native currency. No token approval is needed for the later prepare() step. The server normalizes tokenSymbol to the chain’s nativeSymbol.
You can create a draft without recipients and add them later using replaceRecipients(). This is useful when the recipient list needs to be built incrementally.
Add recipients later
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
request.name | string | Yes | Distribution name |
request.chainId | number | Yes | Target blockchain chain ID |
request.tokenAddress | string | Yes | ERC-20 token contract address. For native currency (ETH, POL, BNB, etc.) pass the zero address 0x0000000000000000000000000000000000000000. |
request.tokenSymbol | string | Yes | Token symbol (e.g. "USDC"). For native currency the server normalizes this to the chain’s nativeSymbol. |
request.notes | string | No | Optional notes or description |
request.listId | string | No | Use recipients from an existing list — mutually exclusive with recipients |
request.recipients | RecipientDto[] | No | Inline recipients [{ address, amount, label?, tags? }] — mutually exclusive with listId |
request.account | string | No | Sender wallet address |
request.idempotencyKey | string | No | Unique key to prevent duplicates — returns existing distribution if key already used |
request.isDeflationary | boolean | No | Set to true if the token has transfer fees (deflationary tokens) |
request.strategy | string | No | Distribution execution strategy |
Returns
Distribution
Created distribution in DRAFT status — editable until you call prepare()
Errors
Validation error
The request is not authorized
You do not have permission to access this resource
Notes
Provide listId or recipients, not both — they are mutually exclusive
If listId is used, the list must exist in your project and all items must have amounts
The distribution stays in DRAFT until you call prepare()
Use idempotencyKey to prevent duplicate drafts on retry
See Also
updateDraft()
Update draft metadata (name, chain, token) before preparing
replaceRecipients()
Replace or set recipients on the draft
prepare()
Generate calldata when ready — transitions to PREPARED
getApproveCalldataForDistribution()
Get exact token approval amount for this distribution
sdk.distributions.updateDraft(id, request)
Update metadata of a distribution in DRAFT status. Change the name, chain, token, or sender address. Only works while the distribution is in DRAFT — once prepared, metadata cannot be changed. To change recipients, use replaceRecipients() instead.
Only distributions in DRAFT status can be updated. Once you call prepare(), the distribution transitions to PREPARED and can no longer be edited.
Switching to native currency
Pass tokenAddress: 0x0000000000000000000000000000000000000000 to switch the draft to native currency (ETH, POL, BNB, …). No approval step is required on the subsequent prepare(). The server normalizes tokenSymbol to the chain’s nativeSymbol.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Distribution ID (must be in DRAFT status) |
request.name | string | No | Updated distribution name |
request.notes | string | No | Updated notes |
request.chainId | number | No | Updated chain ID |
request.tokenAddress | string | No | Updated token address. Pass 0x0000000000000000000000000000000000000000 to target the native currency. |
request.tokenSymbol | string | No | Updated token symbol |
request.account | string | No | Updated sender wallet address |
request.isDeflationary | boolean | No | Whether the token is deflationary |
request.strategy | string | No | Distribution execution strategy |
Returns
Distribution
Updated distribution object (still in DRAFT status)
Errors
Invalid distribution id
The request is not authorized
You do not have permission to access this resource
Distribution not found
Not in DRAFT status
Notes
Only works in DRAFT status — returns an error for any other status
Cannot update recipients through this method — use replaceRecipients() for that
Only include fields you want to change — omitted fields remain unchanged
See Also
createDraft()
Create the draft first
replaceRecipients()
Update recipients (cannot be done through updateDraft)
prepare()
Prepare calldata when editing is complete
sdk.distributions.replaceRecipients(id, request)
Replace all recipients on a DRAFT distribution. This is a full replacement — all existing recipients are removed and replaced with the new list. Use this to update the recipient list after creating a draft, or to correct recipients before preparing.
This is a full replacement, not an append. All existing recipients are removed and replaced with the new array.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Distribution ID (must be in DRAFT status) |
request.recipients | RecipientDto[] | Yes | New recipients array [{ address, amount, label?, tags? }] — replaces all existing |
Returns
Distribution
Updated distribution with new recipient count and total amount
Errors
Invalid distribution id
The request is not authorized
You do not have permission to access this resource
Distribution not found
Not in DRAFT status
Notes
Full replacement — all previous recipients are deleted
Only works in DRAFT status
Each recipient requires address and amount; label and tags are optional
See Also
createDraft()
Create the draft distribution first
prepare()
Prepare calldata after finalizing recipients
sdk.distributions.prepare(id, request?)
Generate transaction calldata for a DRAFT distribution. Transitions the status from DRAFT to PREPARED and returns the updated distribution together with the ready-to-sign transaction batches.
For single-step usage, prefer distribute() — it creates, prepares, and returns calldata all in one call.
Use distribute() instead
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Distribution ID (must be in DRAFT status) |
request.account | string | No | Wallet address that will execute transactions |
request.rpcUrl | string | No | Custom RPC URL for gas estimation |
Returns
DistributeResult
{ distribution, calldata } — the prepared distribution plus the transactions to sign.
Errors
Invalid distribution id
The request is not authorized
You do not have permission to access this resource
Distribution not found
Not in DRAFT status
Notes
Transitions status from DRAFT → PREPARED
The distribution must have recipients (either from createDraft() or replaceRecipients())
Can be re-run on a DRAFT to regenerate transactions (clears previous ones)
Returns { distribution, calldata } — the same shape as distribute(). Use getTransactions() only to fetch persisted batches later.
For native-currency drafts (tokenAddress = 0x0000000000000000000000000000000000000000) no prior approve() is required — the generated calldata already carries the required value on each transaction
See Also
getApproveCalldataForDistribution()
Get token approval before preparing (ERC-20 only)
getTransactions()
Retrieve generated transaction batches after preparing
distribute()
Alternative: create + prepare + get calldata in one call
sdk.distributions.cancel(id)
Cancel a distribution. Works from DRAFT, PREPARED, or IN_PROGRESS status. Distributions in COMPLETED, PARTIALLY_COMPLETED, or FAILED status cannot be cancelled. Note that cancelling an IN_PROGRESS distribution does not reverse already-confirmed on-chain transactions.
Cancelling an IN_PROGRESS distribution stops further batch submissions but does not reverse transactions already confirmed on-chain. Check getTransactions() to see which batches were already confirmed.
Check confirmed transactions
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Distribution ID to cancel |
Returns
Distribution
Distribution with status updated to CANCELLED
Errors
Invalid distribution id
The request is not authorized
You do not have permission to access this resource
Distribution not found
Cannot cancel completed distribution
Notes
Can cancel from DRAFT, PREPARED, or IN_PROGRESS status
Cannot cancel COMPLETED, PARTIALLY_COMPLETED, or FAILED distributions
Cancelling IN_PROGRESS does not reverse already-confirmed on-chain transactions
See Also
get()
Check distribution status before cancelling
distribute()
Create a new distribution to replace the cancelled one
Generated Code