Create multiple campaigns
Everything you need to know to create multiple campaigns in batch on Merkl
While Merkl Studio is ideal for creating standard campaigns, Merkl also provides tooling for campaign creators who need to create multiple (and potentially advanced) campaigns at once.
This feature enables campaign creators to launch multiple campaigns sharing the same base parameters (creator address, reward token, reward chain, start and end time) in a single transaction.
All batch campaign creation methods involve generating a payload. This guide walks you through payload generation. Once you have a payload, refer to Create a Campaign from a Multisig or Gnosis Safe for execution instructions.
Method 1: Override Parameters from Existing Template Campaigns
This method uses existing campaigns as templates. You select past campaigns that closely match what you want, tweak a few parameters, generate a payload, and execute it with a Safe.
1. Retrieve Campaign Templates
Choose past campaigns as close as possible to what you plan to launch. We strongly recommend matching both the campaign type and the distribution type. This minimizes edits and reduces the chance of errors.
Next, find the DatabaseId of the template campaign(s) you will use to derive your payload(s). To do this:
Go to the Merkl app
Select an opportunity and open the "Advanced" tab
Copy your template
DatabaseId
2. Prepare the Campaign Payload
Use this endpoint to build a batch using your selected template DatabaseId value(s) (from Step 1) and updated parameters for all your campaigns.
Request body structure:
{
"creatorAddress": "0x...", // address in checksum format
"rewardToken": "0x...", // checksum format
"distributionChainId": 1, // chain where rewards will be distributed
"startTimestamp": 1756512000, // Unix seconds
"endTimestamp": 1756598400, // Unix seconds
"campaignsParams": {
"9427880006586247706": [// <-- template DatabaseId from Step 1
{
"amount": "85372895000000000000000", // in decimals
"targetToken": "0x...", // or poolAddress / poolId / market / evkAddress
"blacklist": [] // addresses to exclude (checksum)
// ... override more parameters
}
// ...add more campaigns for this same template DatabaseId if needed
]
// Add more DatabaseId, each with its own array of campaigns.
}
}Understanding Parameters
The endpoint uses two types of parameters:
1. Base Parameters (apply to all campaigns in the batch)
These parameters are shared across all campaigns. If you need different values for some campaigns (e.g., different reward token), create a separate payload.
creatorAddress: The address creating the campaignsrewardToken: The token used for rewards across all campaignsdistributionChainId: The chain ID where campaigns will runstartTimestamp: The start date of the campaigns (in Unix timestamp format)endTimestamp: The end date of the campaigns (in Unix timestamp format)
2. Per-Campaign Parameters (specific to each campaign)
These parameters go under campaignsParams and let you customize each campaign. The available fields vary by campaign type. You can view all parameters that you can play on for a specific campaign using this endpoint.
Example: Campaign ID 13756496363331257690 - https://api.merkl.xyz/v4/config/13756496363331257690
Common fields (available for most campaign types):
amount: Amount of rewards to be distributed in the campaigncomputeChainId: ID of a supported chain (see our status page)blacklist: List of addresses to exclude from the campaignwhitelist: List of addresses to include (excluding all others)distributionMethodParameters: Distribution type configuration (see below)
Distribution method options:
Variable reward rate:
{
"distributionMethod": "DUTCH_AUCTION"
} Fixed APR rate:
{
"distributionMethod": "FIX_APR",
"distributionSettings": {
"apr": "0.08",
"targetToken": "0x3048925B3EA5A8C12eeCCcb8810F5F7544dB54af",
"rewardTokenPricing": true,
"targetTokenPricing": true
}
}Capped APR rate:
{
"distributionMethod": "MAX_APR",
"distributionSettings": {
"apr": "1",
"targetToken": "0xA9d17f6D3285208280a1Fd9B94479c62e0AABa64",
"rewardTokenPricing": true,
"targetTokenPricing": true
}
}Best practice: Use a template campaign with the same distribution type as the one you plan to use. While you can change the distribution type (variable, fixed, capped) from the template, we don't recommend this. If you need to do so, please reach out to us directly to ensure your parameters are correct.
For Fixed/Capped APR campaigns, contact us to confirm when rewardTokenPricing and targetTokenPricing should be true or false.
Campaign-specific fields (examples by type):
ERC20LOGPROCESSOR (CampaignType: 18) - Example DatabaseId: 8270489034958466914
targetToken: Address of the token to incentivize holding, or the LP token address for V2 pools
UniV3 (CampaignType: 2) - Example DatabaseId: 9427880006586247706
poolAddress,weightToken0,weightToken1,weightFees
UniV4 (CampaignType: 13) - Example DatabaseId: 14050222419773482936
Morpho single token (CampaignType: 57) - Example DatabaseId: 3011317640800818752
targetToken: Address of the token supplied on any Morpho Market
Euler supply (CampaignType: 12) - Example DatabaseId: 16912425279432080078
evkAddress: Address of the incentivized vault
Euler borrow (CampaignType: 12) - Example DatabaseId: 17331543524323336682
Example
You want to generate a payload for the following campaigns:
2 Uniswap V3 campaigns (template
DatabaseId: 9427880006586247706)1 Morpho (supply at the market level) campaign with a fixed rate of 5% APR (template
DatabaseId: 6937583984928148176)1 Aave (supply side) campaign with a capped APR of 10% (template
DatabaseId: 711211603263558496)
When using the API endpoint, the body will look like this:
{
"creatorAddress": "0xfC6C8e98c381d2320A12822be01F45307266ff5d",
"rewardToken": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"distributionChainId": 1,
"startTimestamp": 1761951600,
"endTimestamp": 1764543600,
"campaignsParams": {
"9427880006586247706": [
{
"amount": "10000000000000000000000",
"poolAddress": "0x2A2C512beAA8eB15495726C235472D82EFFB7A6B",
"weightToken0": 1500,
"weightToken1": 1500,
"weightFees": 7000,
"blacklist": [],
"forwarders": [],
"hooks": []
},
{
"amount": "10000000000000000000000",
"poolAddress": "0x2A2C512beAA8eB15495726C235472D82EFFB7A6B",
"weightToken0": 1500,
"weightToken1": 1500,
"weightFees": 3000,
"blacklist": [],
"forwarders": [],
"hooks": []
}
],
"3011317640800818752": [
{
"amount": "10000000000000000000000",
"distributionMethodParameters": {
"distributionMethod": "FIX_APR",
"distributionSettings": {
"apr": "0.05",
"market": "0xE3190143Eb552456F88464662f0c0C4aC67A77eB",
"rewardTokenPricing": true,
"targetTokenPricing": true,
}
}
}
],
"711211603263558496": [
{
"amount": "10000000000000000000000",
"distributionMethodParameters": {
"distributionMethod": "MAX_APR",
"distributionSettings": {
"apr": "0.1",
"targetToken": "0xE3190143Eb552456F88464662f0c0C4aC67A77eB",
"rewardTokenPricing": true,
"targetTokenPricing": true,
}
}
}
]
}
}Method 2: Using Campaign Configurations
This method allows you to generate a campaign payload using full campaign configurations rather than just overriding parameters from templates.
1. Get Campaign Configuration
Use this endpoint to retrieve a campaign configuration: https://api.merkl.xyz/docs#tag/config/get/v4/config/{id}
2. Adjust Parameters
Modify the parameters in the configuration(s) according to your needs.
3. Preview Your Campaign (Optional)
You can simulate how your campaigns would appear after your payload is executed:
Opportunity preview: Call
https://api.merkl.xyz/v4/config/opportunitywith your campaign config in the body to see opportunity details (name, etc.)TVL preview: Call
https://api.merkl.xyz/v4/config/tvlto estimate TVL
4. Generate the Payload
Call the endpoint https://api.merkl.xyz/v4/config/encode/batch to generate your payload.
In the request body, enter your configurations as a list:
[{
"distributionChainId": 56,
"campaignId": "0xcadb252f36c79aacbd6c96ce2af6cee374c2b0a0929ef6878525bee24987ed5e",
"amount": "5000000000000000000000000",
"computeChainId": 56,
"creator": "0x67C06896Efb9Ce0A14C32B597Fa8bAa3f2659e7D",
"startTimestamp": 1763636400,
"rewardToken": "0xEdBeBe204Ef070B6880E07A28b55edc7748C24BA",
"distributionMethodParameters": {
"distributionMethod": "DUTCH_AUCTION",
"distributionSettings": {}
},
"campaignType": 18,
"endTimestamp": 1764068400,
"blacklist": [],
"whitelist": [],
"forwarders": [],
"targetToken": "0x5029f49585D57ed770D2194841B5A0bE06BFc2ED"
}]Method 3: Pre-Set Campaign Keys (Deprecated)
This method is deprecated. We recommend using Method 1 or Method 2 instead.
This method requires the Merkl team to pre-define campaigns in the backend. You then generate a payload by specifying the amount, dates, and chain for each pre-defined campaign.
Setup
To get started, provide:
The assets you'd like to incentivize
Any customization options you'd like to apply
We'll save this configuration and generate the corresponding keys needed to launch your campaigns in bulk, which we'll share via a GitHub Gist.
Once configured, you'll be able to create multiple campaigns at once, all sharing the same base parameters:
program: Provided by us—the internal ID of your incentive programcreator: The Safe address that will execute the campaign payloadrewardToken: In checksum formatdistributionChainId: The chain where rewards will be distributedstartTimestamp: Campaign start time (Unix timestamp)endTimestamp: Campaign end time (Unix timestamp)
Payload Generation
Generate your campaign payloads using this endpoint.
To use it:
Input the base parameters listed above.
In the request body, paste the JSON file with the keys and placeholder amounts we provided via GitHub Gist (see example below).
For each key:
Replace the placeholder amount with the number of tokens you want to allocate (in raw units).
Use the correct number of decimals (e.g.,
5000000000000000000for 5 tokens if the token has 18 decimals).If you do not plan to incentivize a specific key, remove it entirely from the JSON rather than setting the amount to
0.
Click Send. If the payload is successfully generated, you'll be able to download it. If there's an error, reach out to us and we'll help troubleshoot.
Download the generated payload
Example
Let’s say you’re a chain and want to incentivize:
5 Uniswap pools
2 Euler vaults
You would send us the addresses of the pools and vaults you want to incentivize. Once received, we’ll configure your setup and return the associated keys via a GitHub Gist.
The Gist will follow this format:
{
"ProtocolName1 Asset_Incentivized_1 ProgramName Address": "100000000000000000000",
"ProtocolName1 Asset_Incentivized_2 ProgramName Address": "100000000000000000000",
"ProtocolName2 Asset_Incentivized_3 ProgramName Address": "100000000000000000000"
}For example, it would look like this:
{
"Uniswap USDC/WETH ProgramName 0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640": "100000000000000000000",
"Uniswap WETH/USDT ProgramName 0xc7bbec68d12a0d1830360f8ec58fa599ba1b0e9b": "100000000000000000000",
"Uniswap WBTC/WETH ProgramName 0x4585fe77225b41b697c938b018e2ac67ac5a20c0": "100000000000000000000",
"Uniswap wstETH/WETH ProgramName 0x109830a1aaad605bbf02a9dfa7b0b92ec2fb7daa": "100000000000000000000",
"Uniswap WBTC/cbBTC ProgramName 0xe8f7c89c5efa061e340f2d2f206ec78fd8f7e124": "100000000000000000000",
"Euler Supply WETH ProgramName 0xD8b27CF359b7D15710a5BE299AF6e7Bf904984C2": "100000000000000000000",
"Euler Borrow USDC ProgramName 0xE62055e3f732AB523FB57dDfAbA873a19C8A2CF8": "100000000000000000000"
}The values(100000000000000000000) are placeholder values. When creating your campaigns, you’ll need to replace them with the actual amounts you want to allocate. All amounts must be entered in raw format using the correct token decimals.
Then proceed with the steps outlined in the Payload Generation section above.
Considerations Before Generating a Payload
Minimum Rewards Threshold: Each campaign must meet the minimum hourly token distribution (typically ≥ ~$1/hour). If a campaign in the payload falls below this threshold, the payload will not be generated and will return an error message.
Duplicate Campaigns: If you're reusing the same keys or configuration to increase rewards for an existing campaign, you’ll need to modify the
startTimestamporendTimestampslightly (e.g., by 1 second) to avoid a duplicate campaign error.Payload Size Limitations: If your payload is too large, Safe may fail to execute the transaction. In that case, split your campaigns into multiple smaller batches. Creating up to ~20 campaigns at once typically works fine.
Decimal Precision: Ensure the amounts you distribute match the token's decimals. For example, for an 18-decimal token,
1 token = 1000000000000000000.
Last updated