Skip to main content
A campaign is a reward you run for your users — for example, a trade-profit match that pays each user a multiple of the profit they earned during the campaign window. The Campaigns API lets your server read campaign state and submit the per-user data that the reward is computed from. Everything is scoped to your merchant account — a campaign that isn’t yours returns 404.
On the testnet sandbox? Swap the host for https://api-sandbox.blink.cash.

The model

Two keys run through this API; keeping them straight makes the rest obvious.
  • Qualification is keyed by wallet address. A wallet qualifies by receiving a deposit from you during the campaign window (startDateendDate). Qualification is derived from deposits — it isn’t something you set.
  • Rewards are keyed by your own user id (merchantUserId). A participant owns the user’s matched amount and all payouts, so a per-user cap is tracked in exactly one place even when the user deposited from several wallets. Each participant rolls the user’s qualifying wallets up under it.
You supply the per-user input the reward is computed from (trade profit, today) by pushing participants; Blink computes the match — capped per user, clamped to the remaining campaign budget, and monotonic (a later submission never lowers an existing match). Disbursement is handled separately; these endpoints never move funds.

Authentication

Every request is authenticated with a signed X-Merchant-Authorization header — the same identity credential used across the server-to-server API. See Merchant Authentication for how to build it.
curl https://api.blink.cash/v1/marketings/campaigns \
  -H "X-Merchant-Authorization: $MERCHANT_AUTH"

Amounts and pagination

Every amount is onchain-native: an integer quantity in the token’s smallest unit (base units) as a decimal string, plus the token symbol and its decimals.
{ "amount": "1000000", "currency": "USDC", "decimalPlaces": 6 }
The list endpoints are cursor-paginated. Pass limit (1–100, default 25) and an opaque cursor from the previous page’s nextCursor; a null nextCursor means the last page. Treat the cursor as opaque.

List campaigns

GET https://api.blink.cash/v1/marketings/campaigns Lists your campaigns, newest first.

Query parameters

ParamTypeRequiredDescription
limitintegerNoPage size, 1–100. Default 25.
cursorstringNoOpaque cursor from the previous page’s nextCursor.

Response

{
  "items": [
    {
      "id": "3f8c1e2a-7b4d-4c9a-9f1e-2a7b4d4c9a9f",
      "type": "PER_TRADE_PROFIT_MATCH",
      "status": "ACTIVE",
      "displayName": "2× Your Profits",
      "startDate": "2026-06-01T00:00:00.000Z",
      "endDate": "2026-07-01T00:00:00.000Z",
      "perTradeProfitMatch": {
        "matchMultiplier": 2,
        "maxAmountPerUser": { "amount": "100000000", "currency": "USDC", "decimalPlaces": 6 },
        "maxAmountTotal": { "amount": "10000000000", "currency": "USDC", "decimalPlaces": 6 },
        "currentAmountTotal": { "amount": "2500000000", "currency": "USDC", "decimalPlaces": 6 },
        "payoutChainId": 8453,
        "payoutTokenAddress": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
      },
      "createDate": "2026-05-20T12:00:00.000Z",
      "updateDate": "2026-06-24T18:00:00.000Z"
    }
  ],
  "nextCursor": null
}
FieldTypeDescription
idstringCampaign id — use it in the paths below.
typestringThe campaign mechanic. Currently PER_TRADE_PROFIT_MATCH.
statusstringDRAFT, ACTIVE, PAUSED, or ENDED.
displayNamestringUser-facing campaign name.
startDate / endDatestringISO-8601 campaign window. A wallet qualifies by depositing within it.
perTradeProfitMatch.matchMultipliernumberThe multiple applied to each user’s profit (for example 2 for a 2× match).
perTradeProfitMatch.maxAmountPerUserobjectPer-user reward cap, as a base-unit token amount.
perTradeProfitMatch.maxAmountTotalobjectWhole-campaign reward budget.
perTradeProfitMatch.currentAmountTotalobjectRewards committed so far, against maxAmountTotal.
perTradeProfitMatch.payoutChainIdintegerChain the reward is paid on (numeric chain id).
perTradeProfitMatch.payoutTokenAddressstringToken contract the reward is paid in.

Get a campaign

GET https://api.blink.cash/v1/marketings/campaigns/{campaignId} Returns a single campaign (same shape as a list item). 404 if it doesn’t exist or isn’t yours.

Qualified wallets

GET https://api.blink.cash/v1/marketings/campaigns/{campaignId}/qualifiedWallets With no address filter, returns the paginated list of wallets that deposited during the campaign window. With one or more address params, it’s a point/batch qualification check — only the supplied addresses that qualify come back, so an address absent from the response did not qualify.

Query parameters

ParamTypeRequiredDescription
limitintegerNoPage size, 1–100. Default 25 (for the unfiltered list).
cursorstringNoOpaque cursor from the previous page’s nextCursor.
addressstringNoA wallet address to check. Repeat the param to batch — ?address=0xabc&address=0xdef.
chainIdintegerNoNarrow the check to one chain (numeric chain id, for example 8453).

Response

{
  "items": [
    {
      "address": "0x1111111111111111111111111111111111111111",
      "chainId": 1,
      "chainName": "Ethereum Mainnet",
      "qualified": true,
      "qualifiedDate": "2026-06-20T14:02:11.000Z",
      "participant": { "id": "9c2f0b6e-1d3a-4e5f-8a7b-0c1d2e3f4a5b" }
    }
  ],
  "nextCursor": null
}
FieldTypeDescription
addressstringThe qualifying wallet address.
chainIdintegerThe chain’s numeric id, or null for non-EVM chains.
chainNamestringHuman-readable chain name.
qualifiedbooleanAlways true for a returned wallet (the list only contains qualifying wallets).
qualifiedDatestringISO-8601 of the earliest qualifying deposit in the window.
participantobjectThe participant this wallet maps to, once one exists; null until then. Use participant.id with the participants endpoint to read the reward and payouts.

List participants

GET https://api.blink.cash/v1/marketings/campaigns/{campaignId}/participants Lists your users’ participation in the campaign — their matched reward and payouts. Supply id (a participant id) or merchantUserId (your own user id) to fetch a single participant instead of the list.

Query parameters

ParamTypeRequiredDescription
limitintegerNoPage size, 1–100. Default 25.
cursorstringNoOpaque cursor from the previous page’s nextCursor.
idstring (uuid)NoA participant id (for example from a qualified wallet’s participant.id). Returns that single participant.
merchantUserIdstringNoYour own user id. Alternative to id for fetching a single participant.
statusstringNoFilter the list by QUALIFIED, MATCHED, PAID, or FAILED (ignored when id/merchantUserId is set).

Response

A CampaignParticipantList, or a single participant when id/merchantUserId is set:
{
  "id": "9c2f0b6e-1d3a-4e5f-8a7b-0c1d2e3f4a5b",
  "campaignId": "3f8c1e2a-7b4d-4c9a-9f1e-2a7b4d4c9a9f",
  "merchantUserId": "user-abc",
  "status": "MATCHED",
  "qualifiedDate": "2026-06-20T14:02:11.000Z",
  "tradeProfit": { "amount": "500000000", "currency": "USDC", "decimalPlaces": 6 },
  "amountMatched": { "amount": "1000000000", "currency": "USDC", "decimalPlaces": 6 },
  "maxMatchPerUser": { "amount": "100000000", "currency": "USDC", "decimalPlaces": 6 },
  "associatedWallets": [
    { "address": "0x1111111111111111111111111111111111111111", "chainId": 1, "chainName": "Ethereum Mainnet", "qualifiedDate": "2026-06-20T14:02:11.000Z" },
    { "address": "0x2222222222222222222222222222222222222222", "chainId": 8453, "chainName": "Base", "qualifiedDate": null }
  ],
  "payouts": [],
  "createDate": "2026-06-24T18:00:00.000Z",
  "updateDate": "2026-06-24T18:00:00.000Z"
}
FieldTypeDescription
idstringParticipant id.
merchantUserIdstringYour own user id for this participant.
statusstringQUALIFIED, MATCHED, PAID, or FAILED.
qualifiedDatestringEarliest qualifying deposit across the participant’s wallets.
tradeProfitobjectThe input amount the reward was computed from.
amountMatchedobjectThe reward owed — min(multiplier × profit, maxMatchPerUser, remaining budget).
maxMatchPerUserobjectThe per-user cap snapshot.
associatedWalletsarrayThe user’s wallets rolled up under this participant, each with a qualifiedDate (null if that wallet didn’t qualify).
payoutsarrayDisbursements of the reward, each with its own status trail. Empty until a payout is made.

Submit a participant

POST https://api.blink.cash/v1/marketings/campaigns/{campaignId}/participants Pushes one user’s data into the campaign. Blink computes the reward from the amount you submit, applying the campaign’s multiplier and caps.
  • merchantUserId is the idempotency key. The first submission creates the participant and returns 201; a repeat with the same merchantUserId updates it (refreshing the input amount) and returns 200. Re-submitting is always safe — the reward never decreases and the budget is never double-counted.
  • amount is the measured input the reward is based on (trade profit, today), as a base-unit token amount. Submit it in USDC base units (6 decimals).
  • wallets is the user’s wallets, each { address, chain: { id } } where chain.id is the numeric chain id (for example 1 = Ethereum, 8453 = Base, 137 = Polygon).
  • The campaign must be ACTIVE, and at least one submitted wallet must have a qualifying deposit in the window.

Request body

{
  "merchantUserId": "user-abc",
  "wallets": [
    { "address": "0x1111111111111111111111111111111111111111", "chain": { "id": 1 } },
    { "address": "0x2222222222222222222222222222222222222222", "chain": { "id": 8453 } }
  ],
  "amount": { "amount": "500000000", "currency": "USDC", "decimalPlaces": 6 }
}
curl -X POST https://api.blink.cash/v1/marketings/campaigns/3f8c1e2a-7b4d-4c9a-9f1e-2a7b4d4c9a9f/participants \
  -H "X-Merchant-Authorization: $MERCHANT_AUTH" \
  -H "Content-Type: application/json" \
  -d '{
    "merchantUserId": "user-abc",
    "wallets": [{ "address": "0x1111111111111111111111111111111111111111", "chain": { "id": 1 } }],
    "amount": { "amount": "500000000", "currency": "USDC", "decimalPlaces": 6 }
  }'

Response

201 Created on the first submission, 200 OK on a repeat. The body is the full participant (identical to Get a participant above). With this example — 500 USDC profit on a 2× campaign — amountMatched is 1000000000 (1,000 USDC), subject to the per-user and total caps.
Only users who deposited with Blink during the campaign window earn a reward. If none of a submitted user’s wallets qualified, the request is rejected with 422 NO_QUALIFYING_WALLET and no participant is created — re-submit once the user has a qualifying deposit.

Response codes

CodeMeaning
200OK — read succeeded, or a participant was updated.
201Created — a new participant was inserted.
400Invalid query parameter, cursor, or request body, or a malformed X-Merchant-Authorization header.
401 MERCHANT_AUTHORIZATION_MISSINGThe X-Merchant-Authorization header is missing.
403 MERCHANT_NOT_REGISTERED / MERCHANT_NOT_ACTIVEThe merchant is unknown or not yet approved.
404The campaign doesn’t exist or isn’t yours.
422 CAMPAIGN_NOT_ACTIVEThe campaign isn’t ACTIVE, so it isn’t accepting data.
422 NO_QUALIFYING_WALLETNo submitted wallet has a qualifying deposit in the campaign window.
422The authorization envelope is stale, expired, or its signature is invalid. See Merchant Authentication.
500Internal error.

Error shape

Errors use the standard Blink error envelope:
{
  "error": {
    "code": "NO_QUALIFYING_WALLET",
    "message": "No wallet has a qualifying deposit in the campaign window."
  }
}