BentoSDK

Create a tournament

Check creator eligibility, fund the stage-1 buy-in on-chain, then author a bracket tournament.

By the end: you'll know the full paid create flow: eligibility, vault deposit, then admin create, plus optional protocol-admin ops.

Prerequisites

  • tournamentsBaseUrl and tournamentsAuth (JWT from markets eoaLogin)
  • Markets-host JWT that can call POST /bento/user/wallet/send-transaction (managed smart wallet)
  • Credits or USDC balance for the stage-1 buy-in (testnet: auto-mint faucet after register; register alone does not fund you)
  • Tournament vault + collateral token addresses for the chain (credits vault for stakeAsset: 'credits')

Important: create is not a single POST

The host enforces a minimum stage-1 buy-in of 5 USDC or 5 credits. Free (buyinUsdc: '0') is rejected.

For any stage-1 buy-in greater than 0, the API requires:

FieldMeaning
clientTournamentId24-character hex id you generate before create
creatorDepositTxHashVault deposit tx for that id, stage 1, buy-in amount

Skipping the vault step returns HTTP 400: Paid tournaments require clientTournamentId … deposit to the vault … creatorDepositTxHash.

Fund the vault, then POST /api/tournaments/admin/tournaments. Create persists the tournament as LIVE and registers the creator entry. There is no separate activate call in the create path.

Check eligibility

import { createBentoSdk, jwtAuthProvider, walletAuthProvider } from '@bento.fun/sdk';

const token = process.env.BENTO_JWT!;

const sdk = createBentoSdk({
  baseUrl: process.env.BENTO_URL!, // https://internal-server.bento.fun
  apiKey: process.env.BENTO_BUILDER_API_KEY!,
  tournamentsBaseUrl: process.env.PARLAY_TOURNAMENT_URL,
  auth: walletAuthProvider(() => ({ Authorization: `Bearer ${token}` })),
  tournamentsAuth: jwtAuthProvider({ getAccessToken: () => token }),
});

const stage1BuyinRaw = '5000000000000000000'; // 5 credits (18 decimals)
const eligibility = await sdk.tournaments!.tournaments.getCreatorCreateFlowEligibility({
  wallet: managedWalletAddress,
  stakeAsset: 'credits',
  stage1BuyinRaw,
  minEntriesToActivate: 2,
});
// Inspect eligibility.canProceed before funding

Fund creator buy-in (on-chain), then create

Admin create is not wrapped on createBentoSdk().tournaments. Call the tournaments host admin route with a Bearer JWT.

import { buildVaultDepositCalldata } from '@bento.fun/sdk';
import { encodeFunctionData, erc20Abi, maxUint256 } from 'viem';

const stage1BuyinRaw = '5000000000000000000'; // 5 units
// 12 random bytes as a 24-character hex id.
const clientTournamentId = Array.from(
  crypto.getRandomValues(new Uint8Array(12)),
  (b) => b.toString(16).padStart(2, '0'),
).join('');

const creditsToken = process.env.CREDITS_TOKEN!; // ERC-20
const creditsVault = process.env.TOURNAMENT_VAULT_CREDITS!;

// 1) Approve vault to pull credits (via managed wallet relay)
await fetch(`${process.env.BENTO_URL}/bento/user/wallet/send-transaction`, {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${token}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    to: creditsToken,
    data: encodeFunctionData({
      abi: erc20Abi,
      functionName: 'approve',
      args: [creditsVault, maxUint256],
    }),
    value: '0x0',
  }),
});

// 2) Vault deposit for stage 1 (binds clientTournamentId on-chain)
const depositRes = await fetch(`${process.env.BENTO_URL}/bento/user/wallet/send-transaction`, {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${token}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    to: creditsVault,
    data: buildVaultDepositCalldata(clientTournamentId, 1, '5', 18),
    value: '0x0',
  }),
});
const { txHash: creatorDepositTxHash } = await depositRes.json();

// 3) Author the tournament (tournaments admin route, your Bearer JWT)
const created = await fetch(
  `${process.env.PARLAY_TOURNAMENT_URL}/api/tournaments/admin/tournaments`,
  {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${token}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      ownerWallet: managedWalletAddress,
      name: 'My bracket',
      type: 'PROTOCOL',
      sport: 'Football',
      stakeAsset: 'credits',
      vaultStack: 'credits',
      clientTournamentId,
      creatorDepositTxHash,
      totalStages: 2,
      stages: [
        { stageIndex: 1, buyinUsdc: stage1BuyinRaw, multiplier: 1, isFinal: false },
        { stageIndex: 2, buyinUsdc: stage1BuyinRaw, multiplier: 2, isFinal: true },
      ],
      config: { minEntriesToActivate: 2, stakeAsset: 'credits' },
    }),
  },
);
const { tournament } = await created.json();
// tournament.id === clientTournamentId when paid create succeeds

buyinUsdc is always the raw amount string (the field name is historical). Credits and BSC-testnet USDC use 18 decimals; Base USDC uses 6.

Optional follow-ups (not required for create):

  • Stage builder / markets: admin stage endpoints
  • POST .../admin/tournaments/:id/activate: exists, but create already lands LIVE

Exact body fields vary by format; see OpenAPI or tournamentsAdmin in the SDK API reference.

Participant flow

Once live, other players use the Enter a tournament guide (depositAndEnterTournament, claims). That path deposits against an existing tournament id; creator create deposits before the tournament exists, using clientTournamentId.

Protocol-admin lifecycle

Settlement, disputes, automation, and cancellation run through the gated createTournamentsProtocolAdminClient, a separate opt-in client, not part of createBentoSdk():

import { createTournamentsProtocolAdminClient, jwtAuthProvider } from '@bento.fun/sdk';

const admin = createTournamentsProtocolAdminClient({
  baseUrl: process.env.PARLAY_TOURNAMENT_URL!,
  auth: jwtAuthProvider({ getAccessToken: () => process.env.TOURNAMENTS_ADMIN_JWT! }),
});

await admin.protocol.runAutomation();
await admin.protocol.settleReadyStages();
await admin.protocol.setPayoutRootOnchain(tournamentId);
Phaseadmin.protocol methods
AutomationrunAutomation, settleReadyStages, processTimeouts, getAutomationStatus
SettleforceSettleStage, getPayoutRoot, prepareSetRoot, setPayoutRootOnchain, setRefundRootOnchain
DisputeslistOpenDisputes, getDisputeStats, resolveDispute, processExpiredDisputes
CancelcancelTournament, discardTournament, listCancelRequests, rejectCancelRequest

Full method list: SDK API referenceTournamentsProtocolAdminClient.

On this page