BentoSDK

SDK cookbook

The small set of @bento.fun/sdk methods that cover most integrator apps.

Use this page as the default lookup. For every method + route, see the SDK API reference.

Factory always needs apiKey + auth. Markets user calls also need a Bearer JWT. → Quickstart

Setup (canonical)

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

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, // optional
  auth: walletAuthProvider(() => ({ Authorization: `Bearer ${token}` })),
  // tournamentsAuth: jwtAuthProvider({ getAccessToken: () => token }),
});

Markets — read

GoalCall
List marketssdk.public.listDuels({ page, limit })
Market detailsdk.public.getDuelById({ duelId })duelId only
Protocol statssdk.public.protocolStats.getSummary()

Markets — auth

GoalCall
Challengesdk.public.auth.eoaChallenge({ address, domain })
Loginsdk.public.auth.eoaLogin({ address, signature, timestamp, domain })
Register (new wallet)sdk.public.auth.eoaRegister({ address, signature, timestamp, domain, username })

Sign the server challenge.message verbatim (do not rebuild it).
If eoaLogin returns { exists: false }, call eoaRegister once. JWT address is the managed account. → Authentication

Markets — trade

GoalCall
Quotesdk.user.bets.estimateBuy({ duelId, optionIndex, betAmountUsdc, slippageBps })
Place betsdk.user.placeBetFromEstimate({ estimate, duelId, duelType, bet, optionIndex, betAmount, betAmountUsdc, slippageBps, tokenDecimals }, { idempotencyKey })
Positionsdk.user.bets.getUserShares({ duelId, address }) — managed address
Create marketsdk.user.createDuel(body, { requestId })
Private invitesdk.user.duelInvitations.create / userJoin

Always prefer placeBetFromEstimate over hand-building placeBet (slippage). Amounts are wei. Min stake is 5 units. → Place a bet

Testnet faucet

await sdk.public.faucet.mint({ address: managedAccountAddress });
// ~1000 USDC + ~1000 credits on the managed account (testnet)

Tournaments (when tournamentsBaseUrl is set)

GoalCall
Listsdk.tournaments.tournaments.list({ limit })
Detailsdk.tournaments.tournaments.getById(id)
Enter (HTTP)sdk.tournaments.tournaments.enter(id, { depositTxHash, stakeAsset })
Enter (on-chain helper)sdk.onchain.depositAndEnterTournament(id, { amountUsdc })
Match picks (chips)sdk.tournaments.tournaments.submitPicks(tournamentId, stageId, { picks })
My statussdk.tournaments.tournaments.getMyStatus(id, walletAddress)

Tournament match bets use chips via submitPicks, not placeBet. → Enter a tournament

Sports data (Sportmonks proxy)

Not on createBentoSdk().tournaments — call the tournaments host with fetch. Always pass ttl (live / ended / default / …) and x-builder-api-key (BENTO_BUILDER_API_KEY). Proxy POSTs are capped at 60 req/min/IP; live TTL is clamped to live paths only.

GoalCall
v3 football / motorsportPOST ${PARLAY_TOURNAMENT_URL}/bento/sportmonks/proxy
Cricket v2POST …/bento/sportmonks/cricket-v2/proxy
Fixture snapshotGET …/bento/sportmonks/football/fixture/:id/snapshot
Team squad / season statsGET …/bento/sportmonks/football/team/:id/squad (or …/season-stats)
const sportsHeaders = {
  'Content-Type': 'application/json',
  'x-builder-api-key': process.env.BENTO_BUILDER_API_KEY!,
};

Sportmonks sports data (TTL table + circuit breaker)

Errors (quick)

KindMeaning
auth_errorMissing/invalid JWT, or geo (GEO_BLOCKED — use isGeoblockError)
validation_errorBad body / insufficient balance
rate_limitedHonor retryAfterMs
http_errorOther HTTP failures (correlationId when present)

Common patterns

Intentionally out of scope here

On this page