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
| Goal | Call |
|---|---|
| List markets | sdk.public.listDuels({ page, limit }) |
| Market detail | sdk.public.getDuelById({ duelId }) — duelId only |
| Protocol stats | sdk.public.protocolStats.getSummary() |
Markets — auth
| Goal | Call |
|---|---|
| Challenge | sdk.public.auth.eoaChallenge({ address, domain }) |
| Login | sdk.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
| Goal | Call |
|---|---|
| Quote | sdk.user.bets.estimateBuy({ duelId, optionIndex, betAmountUsdc, slippageBps }) |
| Place bet | sdk.user.placeBetFromEstimate({ estimate, duelId, duelType, bet, optionIndex, betAmount, betAmountUsdc, slippageBps, tokenDecimals }, { idempotencyKey }) |
| Position | sdk.user.bets.getUserShares({ duelId, address }) — managed address |
| Create market | sdk.user.createDuel(body, { requestId }) |
| Private invite | sdk.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)
| Goal | Call |
|---|---|
| List | sdk.tournaments.tournaments.list({ limit }) |
| Detail | sdk.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 status | sdk.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.
| Goal | Call |
|---|---|
| v3 football / motorsport | POST ${PARLAY_TOURNAMENT_URL}/bento/sportmonks/proxy |
| Cricket v2 | POST …/bento/sportmonks/cricket-v2/proxy |
| Fixture snapshot | GET …/bento/sportmonks/football/fixture/:id/snapshot |
| Team squad / season stats | GET …/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)
| Kind | Meaning |
|---|---|
auth_error | Missing/invalid JWT, or geo (GEO_BLOCKED — use isGeoblockError) |
validation_error | Bad body / insufficient balance |
rate_limited | Honor retryAfterMs |
http_error | Other HTTP failures (correlationId when present) |
Intentionally out of scope here
- Full F1 / Polymarket / packs surface — see SDK API reference
- Request JSON schemas — OpenAPI specs