# FAQ (/faq) ## Do I need a wallet or login to read data? [#do-i-need-a-wallet-or-login-to-read-data] No. Public catalog/price/analytics reads need no user JWT. You still pass a Builder `apiKey` to `createBentoSdk` (required by the factory). Use an empty `walletAuthProvider(() => ({}))` for reads. ## Why is my account address different from my wallet? [#why-is-my-account-address-different-from-my-wallet] Registering / logging in provisions a **managed account**. Funds and positions live there, not on your signing wallet. Session field `address` is the managed account. → [Accounts](/concepts/accounts) ## I'm getting `401 Unauthorized`. [#im-getting-401-unauthorized] Markets `sdk.user` needs `Authorization: Bearer ` from `eoaLogin` / `eoaRegister`, not raw `x-wallet-*` headers. → [Authentication](/concepts/authentication) ## I'm getting `400 "Insufficient Credits"`. [#im-getting-400-insufficient-credits] The managed account has no balance. Register/login does **not** fund you. On testnet: `await sdk.public.faucet.mint({ address: managedAccountAddress })`. → [Money](/concepts/money) ## `eoaLogin` returns no token. [#eoalogin-returns-no-token] New wallets get `{ exists: false }`. Call `eoaRegister` once with a `username`, then use the returned `token`. ## How do I bet inside a tournament after I enter? [#how-do-i-bet-inside-a-tournament-after-i-enter] Vault buy-in uses credits/USDC. Match picks use **virtual stage chips** via `submitPicks` (not `placeBet`). → [Enter a tournament](/guides/enter-tournament) ## How do private markets and invites work? [#how-do-private-markets-and-invites-work] Create with `privacyAccess: 'private'`, then `duelInvitations.create`. Invitees must `userJoin` before betting. → [Create a market](/guides/create-market) ## Why are there two URLs / hosts? [#why-are-there-two-urls--hosts] **Markets** (`BENTO_URL`) for bets/packs/create-market; **tournaments** (`PARLAY_TOURNAMENT_URL`) for brackets/F1. Markets-only is enough to start. → [Environments](/concepts/environments) ## `getDuelById` returns 404. [#getduelbyid-returns-404] You passed database `id` instead of `duelId`. Use `duelId` from the list row. ## My amounts look enormous / bets fail with tiny stakes. [#my-amounts-look-enormous--bets-fail-with-tiny-stakes] Amounts are **wei** (18 decimals on BSC/credits). `'10000000000000000000'` is 10 units. Platform minimum is **5** units. ## Should I use `placeBet` or `placeBetFromEstimate`? [#should-i-use-placebet-or-placebetfromestimate] **`placeBetFromEstimate`**. Hand-building from `estimate.min_shares_out` causes slippage 400s on larger bets. → [Place a bet](/guides/place-bet) ## `credits` vs `usdc`: which do I use? [#credits-vs-usdc-which-do-i-use] `credits` = test/play, not geo-gated. `usdc` = real collateral, geo-gated. Set `collateralMode` per market/bet. ## How do I get Sportmonks / live sports data? [#how-do-i-get-sportmonks--live-sports-data] Use the **tournaments-host** Sportmonks proxies (server holds the API token). Send `x-builder-api-key` (`BENTO_BUILDER_API_KEY`), pass a `ttl` profile (`live`, `ended`, `default`, …), poll no faster than the fresh window, and expect **60 req/min/IP** on the proxy POSTs (429 + `Retry-After` if exceeded). → [Sportmonks sports data](/guides/sportmonks-proxy) ## Where should an AI agent start? [#where-should-an-ai-agent-start] [/llms.txt](/llms.txt) → [Quickstart](/quickstart) → [Cookbook](/reference/cookbook) → [Common patterns](/reference/common-patterns). # Introduction (/) **Bento** is an on-chain prediction-markets platform. People bet on real-world outcomes, join tournaments, open packs, and create their own markets, all settled on-chain. **`@bento.fun/sdk`** is the TypeScript SDK for apps, bots, agents, and servers. ## Start here [#start-here] 1. [Quickstart](/quickstart) — zero → first bet on testnet (one script) 2. [Cookbook](/reference/cookbook) — the \~15 methods most apps need 3. [Common patterns](/reference/common-patterns) — `duelId`, wei, errors, auth pitfalls ## What you can build [#what-you-can-build] * **Trade & bet** — browse markets, quote, place bets * **Create markets** — public or private (invite-gated) * **Tournaments** — enter brackets and stake match chips * **Sports data** — Sportmonks fixtures/scores via the rate-limited tournaments proxy ## The model in 60 seconds [#the-model-in-60-seconds] 1. **Reads are open; actions need login.** Catalog needs no user JWT. Betting / creating needs a **Builder API key** + a **user JWT** from a signed login. 2. **You sign with your wallet, trade through a managed account.** Funds live on a different address than your signing key. 3. **Amounts are wei.** Stakes are base units (18 decimals on BSC / credits). ## Install [#install] ```bash npm install @bento.fun/sdk ``` Node.js 18+. Then [Quickstart](/quickstart). ## Building with an AI agent? [#building-with-an-ai-agent] Copy-paste this into the agent, then point it at the live docs: ```text You are integrating @bento.fun/sdk on Bento testnet. 1. Fetch https://docs.bento.fun/llms.txt and follow only these pages in order: - /quickstart (golden path — do not invent a different login/bet flow) - /reference/cookbook - /reference/common-patterns 2. Rules you must not violate: - createBentoSdk always needs apiKey (Builder key) + auth provider - Markets user calls need Authorization: Bearer from eoaLogin/eoaRegister - Use duelId (never list row id) for getDuelById / bets - Amounts are wei (18 decimals on BSC/credits); min bet is 5 units - Prefer placeBetFromEstimate over hand-built placeBet - Fund the managed account address from login (not the EOA) via `sdk.public.faucet.mint` on testnet 3. Prefer /quickstart.md or /llms-full.txt for plain markdown if HTML is noisy. ``` Full dump: [/llms-full.txt](/llms-full.txt). Per-page markdown: append `.md` to any docs URL (e.g. `/quickstart.md`). # Quickstart (/quickstart) **By the end:** you have listed a market, logged in (or registered), funded the managed account, and placed a bet with `placeBetFromEstimate`. > **Agents:** prefer this page + [Cookbook](/reference/cookbook) + [Common patterns](/reference/common-patterns). Plain text: [/llms.txt](/llms.txt). ## 1. Install & env [#1-install--env] ```bash npm install @bento.fun/sdk ``` Node.js 18+. `createBentoSdk` requires a **Builder API key** (`apiKey`). Catalog reads are open without it on the wire, but **login / register** and authenticated calls need `x-builder-api-key` so your app is attributed. [Mint a testnet Builder API key](/concepts/builder-api-key), then: ```bash export BENTO_URL='https://internal-server.bento.fun' export BENTO_BUILDER_API_KEY='bnt_…' ``` Full host table: [Environments](/concepts/environments). ## 2. Zero → first bet (copy-paste) [#2-zero--first-bet-copy-paste] Replace `account` with a viem (or compatible) wallet that can `signMessage`. Run once on testnet. ```ts import { createBentoSdk, walletAuthProvider } from '@bento.fun/sdk'; const baseUrl = process.env.BENTO_URL!; // https://internal-server.bento.fun const apiKey = process.env.BENTO_BUILDER_API_KEY!; // --- public client (reads + login/register) --- const sdk = createBentoSdk({ baseUrl, apiKey, auth: walletAuthProvider(() => ({})), }); // A. List markets — use duelId (on-chain), NEVER id (database row) const { data } = await sdk.public.listDuels({ page: 1, limit: 10 }); const row = data[0]; if (!row?.duelId) throw new Error('no markets'); const duelId = row.duelId; const market = await sdk.public.getDuelById({ duelId }); // B. Challenge → sign the server message verbatim (includes first-party warning) const challenge = await sdk.public.auth.eoaChallenge({ address: account.address, domain: 'yourapp.example', }); const signature = await account.signMessage({ message: challenge.message }); // C. Login, or register once if the wallet is new // JWT `address` = managed (custodial) account where funds live; signing key ≠ that address type AuthRes = { exists?: boolean; token?: string; address?: string }; let authRes = (await sdk.public.auth.eoaLogin({ address: account.address, signature, timestamp: challenge.timestamp, domain: challenge.domain, })) as AuthRes; if (!authRes.exists || !authRes.token) { authRes = (await sdk.public.auth.eoaRegister({ address: account.address, signature, timestamp: challenge.timestamp, domain: challenge.domain, username: `builder-${account.address.slice(2, 8)}`, })) as AuthRes; } const token = authRes.token!; const managedAccountAddress = authRes.address!; // fund THIS address, not your EOA // D. Authenticated client — markets user routes need Bearer JWT const authed = createBentoSdk({ baseUrl, apiKey, auth: walletAuthProvider(() => ({ Authorization: `Bearer ${token}` })), }); // E. Fund on testnet (register does NOT credit you) await authed.public.faucet.mint({ address: managedAccountAddress }); // ~1000 USDC + ~1000 credits on the managed account // F. Estimate → place (always use placeBetFromEstimate; min bet = 5 units) const stake = '10000000000000000000'; // 10 units at 18 decimals — NOT '10' const est = await authed.user.bets.estimateBuy({ duelId, optionIndex: 0, betAmountUsdc: stake, slippageBps: 100, }); if (!est.success) throw new Error('estimate rejected'); await authed.user.placeBetFromEstimate( { estimate: est.estimate, duelId, duelType: market.duelType ?? 'PREDICTION', // PREDICTION | VERSUS bet: market.options?.[0] ?? 'YES', // option label text, not only optionIndex optionIndex: 0, betAmount: stake, betAmountUsdc: stake, slippageBps: 100, tokenDecimals: 18, }, { idempotencyKey: crypto.randomUUID() }, ); // G. Reconcile — poll the managed address (acceptance ≠ settlement) const shares = await authed.user.bets.getUserShares({ duelId, address: managedAccountAddress, }); console.log({ duelId, shares }); ``` ## Rules that trip everyone [#rules-that-trip-everyone] | Pitfall | Fix | | ------------------------------- | ----------------------------------------------------------------------- | | `getDuelById` 404 | Pass `duelId`, not list row `id` | | Amounts look wrong | Use **wei** (18 decimals on BSC/credits): `'10000000000000000000'` = 10 | | `Insufficient Credits` | Fund **managed** address via auto-mint after login | | Slippage 400 after a good quote | Use `placeBetFromEstimate`, not hand-built `min_shares_out` | | `401` on `sdk.user` | Send `Authorization: Bearer ` (not raw `x-wallet-*`) | More: [Common patterns](/reference/common-patterns) · [FAQ](/faq) ## Tournaments (optional second host) [#tournaments-optional-second-host] Only when you need brackets / F1: ```ts import { createBentoSdk, jwtAuthProvider, walletAuthProvider } from '@bento.fun/sdk'; const sdk = createBentoSdk({ baseUrl: process.env.BENTO_URL!, apiKey: process.env.BENTO_BUILDER_API_KEY!, tournamentsBaseUrl: process.env.PARLAY_TOURNAMENT_URL, // see Environments auth: walletAuthProvider(() => ({ Authorization: `Bearer ${token}` })), tournamentsAuth: jwtAuthProvider({ getAccessToken: () => token }), }); const { tournaments } = (await sdk.tournaments!.tournaments.list({ limit: 5, })) as { tournaments: unknown[] }; ``` → [Enter a tournament](/guides/enter-tournament) · [Environments](/concepts/environments) ## Next [#next] # TypeScript SDK (/typescript-sdk) `@bento.fun/sdk` is the core package. It owns domain types, HTTP clients for two API hosts, auth providers, and optional on-chain orchestration. It should be usable from browser apps, React Native, server jobs, scripts, agents, and tests. ## Integration modes [#integration-modes] | Mode | When | Configuration | | ------------------- | ----------------------------------- | ----------------------------------------------------------------------------------- | | **Public reads** | Catalog, analytics, tournament list | `baseUrl` (+ optional `tournamentsBaseUrl`); `apiKey` still required by the factory | | **User HTTP** | Auth, bets, packs, tournament enter | `apiKey` + `auth` + `tournamentsAuth` | | **HTTP + on-chain** | Vault deposit, LP | `onchain: { contracts, wallet }` | ### Browser wallet [#browser-wallet] ```ts import { createBentoSdk, jwtAuthProvider, walletAuthProvider } from '@bento.fun/sdk'; // `token` from eoaLogin / eoaRegister — see Authentication. // `apiKey` required — mint at /concepts/builder-api-key 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 — see Environments auth: walletAuthProvider(() => ({ Authorization: `Bearer ${token}` })), tournamentsAuth: jwtAuthProvider({ getAccessToken: () => token }), }); ``` ### Server prepares calldata, your signer sends [#server-prepares-calldata-your-signer-sends] ```ts import { buildVaultDepositCalldata } from '@bento.fun/sdk'; // amountHuman is whole tokens; decimals match the vault collateral (18 for credits) const data = buildVaultDepositCalldata(tournamentId, 0, '10', 18); const hash = await backendWallet.sendTransaction({ to: creditsVault, // your OnchainContractConfig vault data, }); ``` Or use `sdk.onchain.depositAndEnterTournament(...)` when `onchain` is configured. ### Read-only [#read-only] ```ts 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(() => ({})), }); await sdk.public.listDuels({ page: 1 }); await sdk.tournaments?.tournaments.list({ limit: 5 }); ``` ## Top-level client [#top-level-client] `createBentoSdk()` returns: | Property | Host | Auth | | -------------- | ----------- | ---------------------------------------- | | `public` | Markets | Catalog open; auth needs Builder API key | | `user` | Markets | Builder API key + Bearer JWT | | `tournaments?` | Tournaments | Builder API key + optional JWT / wallet | | `onchain?` | Chain | viem wallet | `createTournamentsSdk()` is available for tournaments-only integrations. ## `sdk.public` (markets host) [#sdkpublic-markets-host] Namespaces on `PublicClient`: | Namespace | Key methods | | -------------------- | ----------------------------------------------- | | `duels` / `markets` | `list`, `getById` (alias naming) | | `packs` | `list`, `getById`, payout/refund proofs | | `leaderboard` | global reads | | `parentMarkets` | read, invite validate | | `portfolio` | account, PnL, positions | | `analytics` | `getPlatformReport` | | `protocolStats` | `getSummary`, `getStats` | | `publicBets` | chart reads | | `auth` | `eoaLogin`, `eoaRegister`, Auth0 flows | | `duelInvitations` | create, validate, list (many routes are public) | | `withdrawalRequests` | pre-validate, list, create | Top-level shortcuts: `listDuels`, `getDuelById`, `listMarkets`, `getMarketById`, `getContests`. List rows expose both `id` (database) and `duelId` (on-chain). Pass **`duelId`** to `getDuelById` / `getMarketById`. ## `sdk.user` (markets host) [#sdkuser-markets-host] | Namespace | Key methods | | ------------------- | ------------------------------------------------------------- | | `bets` | `estimateBuy`, `placeBet`, `sellBet`, `getUserShares`, charts | | `duels` | `createDuel`, `resolve`, contest, participants | | `duelInvitations` | `create`, `userJoin`, `validateInvite`, membership | | `parentMarkets` | create, invite, join, add child | | `packs` | enter, picks, creator flows | | `portfolio` | account reads | | `withdraw` | withdraw, claim winnings/fees | | `polymarket` | discovery, orders, positions | | `referralAnalytics` | referral drill-down | ## `sdk.tournaments` (tournaments host) [#sdktournaments-tournaments-host] Present when `tournamentsBaseUrl` is set. | Namespace | Key methods | | ------------- | ----------------------------------------------------------------------------------------------- | | `tournaments` | `list`, `getById`, `enter`, `submitPicks` / `getPicks` (stage chips), bracket, claims, disputes | | `f1` | dashboards, rounds, predictions, leaderboards | | `lp` | pool, APY, add/remove liquidity | | `auth` | wallet JWT on tournaments host | Sportmonks / other sports **data** proxies are HTTP on the tournaments host (not wired on `createBentoSdk().tournaments`). → [Sportmonks sports data](/guides/sportmonks-proxy) ## `sdk.onchain` [#sdkonchain] Present when `onchain` + `tournamentsBaseUrl` are configured. ```ts sdk.onchain?.depositAndEnterTournament(tournamentId, { amountUsdc: '100' }); sdk.onchain?.claimTournamentPayout(tournamentId); sdk.onchain?.addLiquidity(amount); sdk.onchain?.removeLiquidity(lpAmount); ``` Each `onchain` method resolves to `{ success, txHash?, error? }` and never throws, branch on `result.success`. Calldata builders are also exported: `buildVaultDepositCalldata`, `buildVaultClaimCalldata`. ## Auth providers [#auth-providers] ```ts import { walletAuthProvider, jwtAuthProvider, staticHeadersProvider, } from '@bento.fun/sdk'; ``` | Provider | Headers | Use with | | ----------------------- | ---------------------------------------------------------------------------- | -------------------------- | | `walletAuthProvider` | injected headers (`Authorization: Bearer` markets, `x-wallet-*` tournaments) | `auth` | | `jwtAuthProvider` | `Authorization: Bearer` | `tournamentsAuth` | | `staticHeadersProvider` | fixed headers you supply | `auth` / `tournamentsAuth` | {/* ## Unpublished for security Former opt-in protocol admin factories and examples were removed from public docs. */} ## HTTP vs on-chain flows [#http-vs-on-chain-flows] | Action | HTTP | On-chain | | ---------------- | ----------------------------- | ------------------------------------- | | Tournament enter | `tournaments.enter()` | `onchain.depositAndEnterTournament()` | | Market bet | `user.placeBetFromEstimate()` | Server handles chain via markets host | ## Full API reference [#full-api-reference] * **[Cookbook](/reference/cookbook)** — the \~15 methods most apps need * **[SDK API reference](/reference/sdk-api)** — public / user / tournaments methods + routes * **[OpenAPI specs](/reference/openapi)** — pinned JSON for agents, LLMs, and types (`/openapi/*.json`) Request/response shapes also ship as TypeScript types in `@bento.fun/sdk`. ### Documentation layers [#documentation-layers] | Layer | Purpose | | ------------------------------------------------------- | ------------------------------------ | | [Quickstart](/quickstart) + [Guides](/guides/place-bet) | End-to-end flows | | [Cookbook](/reference/cookbook) | Curated method shortlist | | This page | Clients, auth, namespaces, on-chain | | [SDK API reference](/reference/sdk-api) | Exhaustive method + HTTP route index | ## Intentionally excluded [#intentionally-excluded] Not exposed in the public SDK: * `/bento/bot/*` * Inbound webhooks * `/api/meta/*`, `/api/config/keys` ## Wallet-agnostic design [#wallet-agnostic-design] The SDK depends on viem-compatible wallets for on-chain paths only. Privy, Wagmi, Turnkey, and server signers belong in your app layer. # Accounts & wallets (/concepts/accounts) The surprise that trips up most first integrations: **the address you sign with is not the address your funds and positions live on.** ```text your wallet (EOA) Bento managed account 0xAAA… (your key) 0xBBB… (provisioned for you) │ ▲ └──────── sign the login ────────────┘ ↳ funds · bets · positions · payouts live here ``` When you register or log in, Bento provisions a **managed account** for your signing key. You keep custody of your key; your trading runs through the managed account. Register / login does **not** fund that account. On testnet, call `sdk.public.faucet.mint({ address: managedAccountAddress })` to receive USDC and credits. → [Money](/concepts/money#testnet-faucet-register-does-not-fund-you) ## What it means for you [#what-it-means-for-you] * **Read positions by the managed address**, not your signing key: ```ts // `address` is your managed account (returned with your session), not your EOA const shares = await sdk.user.bets.getUserShares({ duelId, address }); ``` * **Fund the managed account** before bets or paid tournament flows (testnet: auto-mint faucet). An empty account returns `400 Insufficient Credits`. * **Sign with your key, settle on the managed account.** You sign the login (and any wallet-signed payloads); bets and payouts execute against the managed account. Your session response (`eoaLogin` / `eoaRegister`) includes the managed address; keep it around. ## Related [#related] * [Authentication](/concepts/authentication) · [How Bento works](/concepts/how-bento-works) · [Money](/concepts/money) # Authentication (/concepts/authentication) Bento uses two layers: 1. **Builder API key** identifies your app (`apiKey` → `x-builder-api-key`). Required by `createBentoSdk`. For hackathons, [generate a testnet key](/concepts/builder-api-key). 2. **User JWT** identifies the user. Attach it with `walletAuthProvider` / `jwtAuthProvider` on `createBentoSdk({ auth })`. Use `baseUrl: 'https://internal-server.bento.fun'` on testnet. You store the session. Bento does not keep platform logins for you. ## EOA (canonical) [#eoa-canonical] Sign the login message, then login — or register once if the wallet is new: ```ts import { createBentoSdk, walletAuthProvider } from '@bento.fun/sdk'; const sdk = createBentoSdk({ baseUrl: 'https://internal-server.bento.fun', apiKey: process.env.BENTO_BUILDER_API_KEY!, auth: walletAuthProvider(() => ({})), }); const challenge = await sdk.public.auth.eoaChallenge({ address: account.address, domain: 'yourapp.example', }); const signature = await account.signMessage({ message: challenge.message }); type AuthRes = { exists?: boolean; token?: string; address?: string }; let res = (await sdk.public.auth.eoaLogin({ address: account.address, signature, timestamp: challenge.timestamp, domain: challenge.domain, })) as AuthRes; // New wallet: eoaLogin returns { exists: false } (HTTP 200, no token) if (!res.exists || !res.token) { res = (await sdk.public.auth.eoaRegister({ address: account.address, signature, timestamp: challenge.timestamp, domain: challenge.domain, username: `builder-${account.address.slice(2, 8)}`, // mainnet also requires inviteCode })) as AuthRes; } const token = res.token!; const managedAccountAddress = res.address!; // custodial — funds live here const authed = createBentoSdk({ baseUrl: 'https://internal-server.bento.fun', apiKey: process.env.BENTO_BUILDER_API_KEY!, auth: walletAuthProvider(() => ({ Authorization: `Bearer ${token}` })), }); ``` | Field on session | Meaning | | ---------------- | ------------------------------------------ | | `token` | Bearer JWT for `sdk.user` | | `address` | **Managed account** (balances / positions) | | Signing wallet | Your EOA — proves identity only | Register / login does **not** fund you. On testnet, call `sdk.public.faucet.mint({ address: managedAccountAddress })` next. → [Money](/concepts/money#testnet-faucet-register-does-not-fund-you) ## Auth providers [#auth-providers] | Provider | Headers | Use with | | ----------------------- | --------------------------------------------------------------- | ----------------- | | `walletAuthProvider` | Injected headers — put `Authorization: Bearer` here for markets | `auth` | | `jwtAuthProvider` | `Authorization: Bearer` | `tournamentsAuth` | | `staticHeadersProvider` | Fixed headers you supply | either | ## Tournaments host [#tournaments-host] `sdk.tournaments.auth.login` uses the same signed login message and returns a tournaments-host JWT. Prefer Bearer via `tournamentsAuth` over legacy `x-wallet-*` headers. # Builder API Key (/concepts/builder-api-key) The Builder API key identifies **your application** (layer 1). It is separate from the **user JWT** (layer 2) you get from EOA login. Catalog reads skip the builder key; **login / register** and user actions require it. Pass both when calling authenticated routes: ```ts import { createBentoSdk, walletAuthProvider } from '@bento.fun/sdk'; const sdk = createBentoSdk({ baseUrl: 'https://internal-server.bento.fun', apiKey: process.env.BENTO_BUILDER_API_KEY!, // required: x-builder-api-key auth: walletAuthProvider(() => ({ Authorization: `Bearer ${userJwt}` })), }); ``` ## Generate a key (testnet) [#generate-a-key-testnet] Use the form below during hackathons. Keys are minted **only** on the testnet markets host (`internal-server.bento.fun`). This site never creates mainnet keys. Copy the key immediately. It is shown once. Store it as `BENTO_BUILDER_API_KEY` (or `NEXT_PUBLIC_BUILDER_API_KEY` for browser apps) and pass it as `apiKey` to `createBentoSdk`. ## How it fits with auth [#how-it-fits-with-auth] | Layer | Header | Purpose | | ------------- | ------------------------- | ------------------------------- | | Builder (app) | `x-builder-api-key` | Who is calling (your SDK / app) | | User | `Authorization: Bearer …` | Which Bento user is acting | See [Authentication](/concepts/authentication) for EOA user login. ## Rate limits [#rate-limits] Your key has a request budget, and it is **shared across every endpoint the key calls**. Exceeding it returns `429` for everything that key is doing, not just the call that tripped it. | Key | Default budget | | ---------------------------- | --------------------- | | Self-serve testnet key | 300 requests / minute | | Key issued by the Bento team | 120 requests / minute | Both are configurable per app; ask if you need more. Two things worth doing: * **Handle the 429.** It arrives as `kind: 'rate_limited'`; wait at least `getRetryAfterMs(err)` before retrying. See [Errors](/reference/common-patterns#errors). * **Pace your calls** so you rarely see one. The SDK can throttle per endpoint locally. See [Rate limits](/reference/common-patterns#rate-limits). Treat the key as a secret: it identifies your app, carries your budget, and belongs server-side, never in client bundles. ## Mainnet [#mainnet] Production / mainnet Builder keys are issued by Bento ops, not this form. Contact the team if you need a mainnet key after the hackathon. # Environments & env vars (/concepts/environments) Start on **testnet**. You only need the markets host for catalog reads and betting. Add the tournaments host when you build brackets or F1. ## Hosts (testnet) [#hosts-testnet] | Host | Env var | URL | What it unlocks | | --------------- | ----------------------- | --------------------------------------------------------- | ----------------------------------------------------------------------- | | **Markets** | `BENTO_URL` | `https://internal-server.bento.fun` | Catalog, login, bets, create market, portfolio | | **Tournaments** | `PARLAY_TOURNAMENT_URL` | `https://bento-fun-tournaments-backend-3nku.onrender.com` | Bracket tournaments, F1, LP, vault enter, **Sportmonks sports proxies** | Omit `PARLAY_TOURNAMENT_URL` until you need tournaments — `sdk.tournaments` / `sdk.onchain` stay `undefined`. ## Required for the SDK [#required-for-the-sdk] | Variable | Required | Purpose | | ----------------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | | `BENTO_BUILDER_API_KEY` | **Yes** | App identity (`apiKey` → `x-builder-api-key`). [Mint a testnet key](/concepts/builder-api-key). Required on tournaments sports proxies too. | | `BENTO_URL` | Yes for live calls | Markets host origin (no trailing slash). | | `PARLAY_TOURNAMENT_URL` | Only for tournaments | Tournaments host origin. | ```bash export BENTO_URL='https://internal-server.bento.fun' export BENTO_BUILDER_API_KEY='bnt_…' # from /concepts/builder-api-key # optional — only when you need tournaments: export PARLAY_TOURNAMENT_URL='https://bento-fun-tournaments-backend-3nku.onrender.com' ``` ## Mainnet [#mainnet] Mainnet markets host and Builder keys are issued by Bento ops (not the docs form). Contact the team after the hackathon if you need production credentials. ## Related [#related] * [Quickstart](/quickstart) — zero → first bet on testnet * [Two API hosts](/concepts/two-hosts) — why the split exists # Glossary (/concepts/glossary) | Term | What it means | | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Duel** | A market. The SDK uses "duel" (the on-chain term) and "market" interchangeably. `sdk.public.duels` and `sdk.public.markets` are aliases. | | **`privacyAccess`** | `'public'` (catalog + anyone can bet after bootstrap) or `'private'` (invite-gated; join required to bet). | | **Duel invitation** | Code for a private market (`duelInvitations.create` / `userJoin`). Viewing with a code ≠ membership. | | **`duelId` vs `id`** | Every market row has both. `duelId` is the on-chain id (**use it** for detail, bets, and charts). `id` is the server's internal record id. | | **Outcome / option** | A side of a market. Bets need both `optionIndex` (0 or 1) and `bet` (the option **label text**, e.g. `YES` / `NO` or versus `optionA` / `optionB` text). Prefer reading labels from `market.options`. | | **Stake** | The amount you put on a bet, in collateral base units (wei). | | **Shares** | What a bet buys: your position size in a market. | | **Pack** | A bundled points game on the markets host. | | **Parent market** | A grouped event with several child markets. | | **Tournament** | A bracket competition with a vault, entries, and Merkle payouts. | | **Tournament chips** | Virtual per-stage stake units (often 1000 at stage start). Used with `submitPicks` on match markets; not USDC/`placeBet`. | | **Pick** | A chip stake on one tournament match market: `{ marketId, side, stakeChips }`. | | **Auto-mint / faucet** | `sdk.public.faucet.mint` (`POST /bento/auto-mint/mint`) that credits \~1000 USDC + \~1000 credits to the managed account on testnet. Register alone does not fund you. | | **Managed account** | The address Bento provisions for your signing key, where your funds and positions live (not your wallet's own address). | | **EOA / signing key** | The wallet you sign the login message with. It proves identity; it isn't where funds sit. | | **Credits vs USDC** | Two collateral stacks (`collateralMode`). Credits are not geo-gated; the USDC stack is. | | **wei** | Amounts are token base units: 18 decimals on BSC / credits. `'10000000000000000000'` is 10, not `'10'`. | | **Markets host / tournaments host** | The two hosts: `baseUrl` vs `tournamentsBaseUrl`. | | **Sportmonks proxy** | Rate-limited tournaments-host routes (`/bento/sportmonks/*`) that cache Sportmonks data; pass `ttl` (`live` / `ended` / `default`). Token stays server-side. | | **Bearer JWT** | The token from `eoaLogin` you send as `Authorization: Bearer` for markets `sdk.user` calls. | # How Bento works (/concepts/how-bento-works) Five things explain almost everything about building on Bento. Read this once and the rest of the docs will click. ```text your app ──HTTP──▶ Bento API ──▶ BSC (on-chain settlement) │ ▲ └── sign login ────┘ → JWT (Bearer), sent on sdk.user calls ``` ## 1. Markets live on-chain [#1-markets-live-on-chain] Bento markets and tournament vaults settle on **BSC**. You don't manage contracts to get started. You call HTTP methods, and for a few flows (vault deposits) you submit a transaction with your own wallet. ## 2. Two hosts, one SDK [#2-two-hosts-one-sdk] `createBentoSdk()` talks to two hosts: * **Markets host** (`baseUrl`): bets, packs, create-market, portfolio. * **Tournaments host** (`tournamentsBaseUrl`): bracket tournaments and F1. You only need the markets host to start. Add the tournaments host when you reach tournaments. → [Two API hosts](/concepts/two-hosts) ## 3. Catalog is open; login and actions need a Builder key + user JWT [#3-catalog-is-open-login-and-actions-need-a-builder-key--user-jwt] Catalog/price/analytics reads need no auth. **Login and register** need a **Builder API key** (`apiKey` → `x-builder-api-key`). Acting as a user (place a bet, create a market) also needs a **JWT** from that login. `createBentoSdk` requires `apiKey` so you're ready for both. → [Builder API key](/concepts/builder-api-key) · [Authentication](/concepts/authentication) ## 4. You sign with your key, but trade through a managed account [#4-you-sign-with-your-key-but-trade-through-a-managed-account] When you register or log in, Bento provisions a **managed account** whose address is *different* from your signing wallet. Your balances, positions, and payouts live there. Register does **not** fund that account; on testnet call the auto-mint faucet next. → [Accounts & wallets](/concepts/accounts) · [Money](/concepts/money#testnet-faucet-register-does-not-fund-you) ## 5. Accepted ≠ settled [#5-accepted--settled] A write returns when the server **accepted** it, not when the chain finalized. Poll a read to confirm the result. → [Acceptance vs finality](/concepts/mutations) *** Next: [Quickstart](/quickstart) for the zero → first-bet script, or [Environments](/concepts/environments) for hosts/env. More detail (optional): [Two hosts](/concepts/two-hosts) · [Accounts](/concepts/accounts) · [Money](/concepts/money) · [Mutations](/concepts/mutations) · [Glossary](/concepts/glossary) # Money (credits, USDC & amounts) (/concepts/money) Two things to get right before you move money: which **collateral stack** you're on, and that **amounts are base units (wei)**, not whole tokens. ## Credits vs USDC [#credits-vs-usdc] Markets and bets carry a `collateralMode` of `'credits'` or `'usdc'`: | Stack | What it is | Geo | | ------------- | ---------------------------------------- | --------------------------------------------------------------------------------------------------- | | **`credits`** | a platform-managed balance (test / play) | not geo-gated | | **`usdc`** | real on-chain USDC collateral | **geo-gated**: restricted regions get `403` with code `GEO_BLOCKED` (detect with `isGeoblockError`) | Either way, your **managed account** needs a funded balance, or writes fail with `400 "Insufficient Credits"` (or insufficient balance). Fund it before trading. ## Testnet faucet (register does not fund you) [#testnet-faucet-register-does-not-fund-you] `eoaRegister` / `eoaLogin` only provision the managed account and return a JWT. They do **not** credit USDC or credits. On testnet, mint via the SDK faucet after you have the managed address: ```ts await sdk.public.faucet.mint({ address: managedAccountAddress, // from eoaLogin / eoaRegister — not your EOA }); // Testnet success: ~1000 USDC + ~1000 Credits on the managed account. ``` Optional readiness check: `await sdk.public.faucet.status()`. Without this (or another funding path), `placeBet`, paid tournament create/enter, and similar writes fail with insufficient balance. ## Amounts are in wei [#amounts-are-in-wei] `betAmount` and `betAmountUsdc` are **base units of the collateral token**, not whole tokens. On BSC / credits that's **18 decimals**: ```ts const tenUnits = '10000000000000000000'; // 10 units, NOT '10' ``` Passing `'10'` means 10 wei, effectively zero. Scale by the token's decimals (e.g. viem's `parseUnits('10', 18)`), or use `collateralDecimals` from your `onchain.contracts`. ## Related [#related] * [Accounts & wallets](/concepts/accounts): where the balance lives * [Place a bet](/guides/place-bet) * [Enter a tournament](/guides/enter-tournament): vault buy-in vs in-tournament chips # Acceptance vs finality (/concepts/mutations) When you place a bet or create a market, the call returns once the server **accepts** it, not when the chain has settled. It may still validate, queue, or submit the transaction. So the pattern is always: write, then confirm. ## Confirm the result [#confirm-the-result] 1. **Poll a read**: `getUserShares`, tournament detail / entry status 2. **`waitFor*` helpers**: `waitForTournamentEntry` (takes a status-getter) There's no universal `waitForFinality()`; the server doesn't expose a status API for every write. ## Idempotency [#idempotency] Pass a **caller-stable** `idempotencyKey` on writes so a retry can't double-submit: ```ts await sdk.user.placeBetFromEstimate(input, { idempotencyKey: 'bet-unique-id', // stable per logical bet, not Date.now() }); ``` ## On-chain writes [#on-chain-writes] `sdk.onchain.*` sends the transaction via your viem wallet and returns `{ success, txHash?, error? }`. It **never throws**, so branch on `success`: ```ts import { waitForTournamentEntry } from '@bento.fun/sdk'; const res = await sdk.onchain!.depositAndEnterTournament(tournamentId, { amountUsdc: '100', }); if (!res.success) throw new Error(res.error); await waitForTournamentEntry(() => sdk.tournaments!.tournaments.getMyStatus(tournamentId), ); ``` ## Related [#related] * [How Bento works](/concepts/how-bento-works) · [Place a bet](/guides/place-bet) # Two API hosts (/concepts/two-hosts) Bento runs on two hosts, and the SDK routes each call to the right one. **You only need the markets host to start**. Add the tournaments host when you reach tournaments. ## Markets host: `baseUrl` [#markets-host-baseurl] `https://internal-server.bento.fun` (testnet). Where you start: bets, packs, create-market, and portfolio. | Client | Products | Auth | | ----------------- | ----------------------------- | ---------------------------- | | `sdk.public` | catalog, packs, analytics | None (open reads) | | `sdk.public.auth` | login, register | Builder API key | | `sdk.user` | bets, create market, withdraw | Builder API key + Bearer JWT | ## Tournaments host: `tournamentsBaseUrl` [#tournaments-host-tournamentsbaseurl] Set `PARLAY_TOURNAMENT_URL` only when you need bracket tournaments or F1. Testnet default: `https://bento-fun-tournaments-backend-3nku.onrender.com`. Omit it and `sdk.tournaments` / `sdk.onchain` are `undefined`. Full table: [Environments](/concepts/environments). | Client | Products | | -------------------------- | --------------------------------------------------------------------------------------------------------------------- | | `sdk.tournaments` | bracket tournaments, F1, LP pool | | HTTP `/bento/sportmonks/*` | Sportmonks sports data proxies (Builder key + rate limits; not on the SDK client) — [guide](/guides/sportmonks-proxy) | ## Auth differs per host [#auth-differs-per-host] * **Builder key**: required `apiKey` on `createBentoSdk` → `x-builder-api-key` (also required on tournaments sports proxies). [Mint one](/concepts/builder-api-key). * **Markets user**: Bearer JWT from `eoaLogin` / `eoaRegister`, via `walletAuthProvider`. * **Tournaments user**: JWT on `tournamentsAuth` (prefer over legacy `x-wallet-*` headers). ```ts const sdk = createBentoSdk({ baseUrl: '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 }), }); ``` Building only tournaments? Use `createTournamentsSdk({ baseUrl: tournamentsBaseUrl, auth })`. # Create a market (/guides/create-market) **By the end:** you'll have submitted a new market and read it back from the catalog. ## Prerequisites [#prerequisites] * Markets host URL (`baseUrl`) * Bearer JWT from `eoaLogin` on `createBentoSdk` (creator wallet) * Creator wallet authorized on `createBentoSdk` (creation submits an on-chain transaction via your managed wallet) ## Flow [#flow] ```ts import { createBentoSdk, 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!, auth: walletAuthProvider(() => ({ Authorization: `Bearer ${token}` })), }); // 1. Create (HTTP 201: on-chain submission accepted) // startTime must be at least 5 minutes ahead (the on-chain minimum). Public markets // also need time to bootstrap (reach their liquidity threshold before startTime, or // they are cancelled), so give them a wider window (~30 min). Private markets skip // bootstrapping, so ~5 minutes is enough. A value near the floor can pass HTTP // validation but revert on-chain; the managed wallet's pre-flight simulation surfaces // that as a 500 ("Pre-flight simulation failed"). const start = Date.now() + 31 * 60_000; // public: bootstrap window; private: ~5 min const result = await sdk.user.createDuel( { question: 'Will Team A win?', type: 'prediction', // or 'versus' with optionA / optionB category: 'Football', // one of: Cricket, Football, Basketball, American Football, Tennis, Baseball, Hockey, Formula 1 description: 'Optional context for traders', // Optional fields you can also send: // optionA / optionB (required for versus), coverImageUrl, tags (e.g. ['Premier League']) startTime: new Date(start).toISOString(), endTime: new Date(start + 2 * 3600_000).toISOString(), // required; must be after startTime privacyAccess: 'public', collateralMode: 'usdc', // or 'credits' tags: ['Premier League'], }, { requestId: `create-${Date.now()}` }, ); // `result.kind` is always 'accepted' when createDuel resolves; real failures THROW a // BentoSdkErrorException: wrap createDuel in try/catch and inspect err.sdkError instead. const { duelId, txHash } = result.raw; console.log({ duelId, txHash }); // 2. Poll until the market appears in catalog (eventual consistency) let detail; for (let i = 0; i < 10; i++) { try { detail = await sdk.public.getDuelById({ duelId }); break; } catch { await new Promise((r) => setTimeout(r, 2000)); } } ``` Use `duelId` from the response, not the `id` field from list rows. See [Common patterns](/reference/common-patterns). ## Private markets (invite → join → bet) [#private-markets-invite--join--bet] Private markets are hidden from public catalogs. Anyone with a valid invite can **view** the market; only members (or the creator) can **bet**. Membership is on-chain: the invitee must join before `placeBet`. ### 1. Create with `privacyAccess: 'private'` [#1-create-with-privacyaccess-private] Private markets skip the public bootstrap window, so `startTime` \~5–8 minutes ahead is enough (still at least 5 minutes for the on-chain floor): ```ts const start = Date.now() + 8 * 60_000; const created = await sdk.user.createDuel( { question: 'Private: will Team A win?', type: 'prediction', category: 'Football', startTime: new Date(start).toISOString(), endTime: new Date(start + 2 * 3600_000).toISOString(), privacyAccess: 'private', collateralMode: 'usdc', }, { requestId: `private-${Date.now()}` }, ); const { duelId } = created.raw; ``` Creating a private duel does **not** auto-create an invite. The creator creates one next (the web app does this from the share modal). ### 2. Create an invite code (creator) [#2-create-an-invite-code-creator] Invite codes are 4–32 alphanumeric characters (`_`, `-` allowed). The web UI uses a **10-character** uppercase code: ```ts const inviteCode = Math.random().toString(36).substring(2, 12).toUpperCase(); await sdk.user.duelInvitations.create({ duelId, inviteCode, inviterAddress: managedAccountAddress, // creator's managed account expiresAt: 0, // never maxUses: 0, // unlimited }); // Share: https://your.app/market/{duelId}?invite={inviteCode} ``` ### 3. Invitee: view, join, then bet [#3-invitee-view-join-then-bet] ```ts // Optional pre-check (public) await sdk.user.duelInvitations.validateInvite({ inviteCode, userAddress: inviteeManagedAddress, }); // View with invite (membership not required yet) await sdk.public.getDuelById({ duelId, inviteCode, userAddress: inviteeManagedAddress, }); // Join — JWT must be the invitee's session (on-chain membership + DB) await sdk.user.duelInvitations.userJoin({ inviteCode }); await sdk.user.duelInvitations.checkMembership(duelId, inviteeManagedAddress); // Same bet flow as public markets (invitee must be funded — testnet faucet) const stake = '10000000000000000000'; // 10 units const est = await sdk.user.bets.estimateBuy({ duelId, optionIndex: 0, betAmountUsdc: stake, slippageBps: 100, }); await sdk.user.placeBetFromEstimate( { estimate: est.estimate, duelId, duelType: 'PREDICTION', // or market.duelType bet: 'YES', // option label text from market.options[0] optionIndex: 0, betAmount: stake, betAmountUsdc: stake, slippageBps: 100, tokenDecimals: 18, }, { idempotencyKey: crypto.randomUUID() }, ); ``` | Step | Who | SDK | | ---------------------- | ---------------- | --------------------------------------------------- | | Create private duel | Creator | `user.createDuel` (`privacyAccess: 'private'`) | | Create invite | Creator | `user.duelInvitations.create` | | Validate (optional) | Invitee | `user.duelInvitations.validateInvite` | | Join (required to bet) | Invitee | `user.duelInvitations.userJoin` | | Bet | Invitee (member) | `estimateBuy` → `placeBet` / `placeBetFromEstimate` | `userJoin` is the preferred join path (matches the web app). Legacy `duelInvitations.join` still exists but is not what the frontend uses. ## Parent markets (grouped events) [#parent-markets-grouped-events] For multi-market events (e.g. a match with several child markets): ```ts const parentStart = Date.now() + 31 * 60_000; // public: bootstrap window (see above) const parent = await sdk.user.createParentMarket( { parentQuestion: 'Match outcome?', category: 'Football', startTime: new Date(parentStart).toISOString(), endTime: new Date(parentStart + 2 * 3600_000).toISOString(), privacyAccess: 'private', // private parent: backend auto-creates an invite code markets: [/* ParentMarketChildDto[]: child question / type / options */], }, { requestId: `parent-${Date.now()}` }, ); // Extra invites (creator), or use the code returned on private create: await sdk.user.createParentMarketInvitation({ parentMarketId: parent.raw.parentMarketId, }); // Invitee joins the parent (propagates membership to private children): await sdk.user.joinParentMarket({ inviteCode: 'PARENTCODE' }); // then placeBet on a child duelId ``` Request bodies are defined in the [Markets OpenAPI](/reference/openapi) schema (`CreateDuelDto`, parent-market DTOs). ## After creation [#after-creation] | Task | SDK call | | ----------------- | -------------------------------------------------------------------------------------- | | Resolve outcome | `sdk.user.duels.resolve` | | Contest / dispute | `sdk.user.duels.submitContest` | | Public reads | `sdk.public.getDuelById({ duelId })` (private: pass `inviteCode` and/or `userAddress`) | | Invite / join | `sdk.user.duelInvitations.*` | HTTP acceptance does not mean the chain has finalized. See [Mutation semantics](/concepts/mutations). ## Related [#related] * [Place a bet](/guides/place-bet): trade on an existing market (private: join first) * [Money](/concepts/money): testnet faucet before betting * [SDK API reference](/reference/sdk-api): `sdk.user.duels.createDuel`, `sdk.user.duelInvitations` * [OpenAPI specs](/reference/openapi): full request/response schemas # Create a tournament (/guides/create-tournament) **By the end:** you'll know the paid create prerequisites — eligibility and vault deposit. Tournament authoring APIs are not published in these docs. ## Prerequisites [#prerequisites] * `tournamentsBaseUrl` and `tournamentsAuth` (JWT from markets `eoaLogin`) * Markets-host JWT that can call `POST /bento/user/custodial/tournament-vault/deposit` (managed smart wallet; raw `send-transaction` was removed) * Credits or USDC balance for the stage-1 buy-in (testnet: [auto-mint faucet](/concepts/money#testnet-faucet-register-does-not-fund-you) after register; register alone does not fund you) * Tournament vault address for the chain (credits vault for `stakeAsset: 'credits'`) ## Important: create is not a single POST [#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, create requires: | Field | Meaning | | ---------------------- | ------------------------------------------------------ | | `clientTournamentId` | 24-character hex id you generate before create | | `creatorDepositTxHash` | Vault `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`. ## Check eligibility [#check-eligibility] ```ts 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) [#fund-creator-buy-in-on-chain] ```ts import { tournamentMongoIdToBytes32 } from '@bento.fun/sdk'; 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 creditsVault = process.env.TOURNAMENT_VAULT_CREDITS!; // Custodial path: approve (if needed) + TournamentVault.deposit in one call. // Raw /user/wallet/send-transaction was removed in the audit hardening. const depositRes = await fetch( `${process.env.BENTO_URL}/bento/user/custodial/tournament-vault/deposit`, { method: 'POST', headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json', 'x-builder-api-key': process.env.BENTO_BUILDER_API_KEY!, }, body: JSON.stringify({ vaultAddress: creditsVault, tournamentIdBytes32: tournamentMongoIdToBytes32(clientTournamentId), stageIndex: '1', amountRaw: stage1BuyinRaw, stakeAsset: 'credits', }), }, ); const { txHash: creatorDepositTxHash } = await depositRes.json(); // Keep clientTournamentId + creatorDepositTxHash for the authoring step. ``` `buyinUsdc` is always the raw amount string (the field name is historical). Credits and BSC-testnet USDC use 18 decimals; Base USDC uses 6. {/* ## Author the tournament — unpublished for security Tournament authoring HTTP routes and privileged SDK factories are intentionally not documented on docs.bento.fun. */} ## Participant flow [#participant-flow] Once a tournament is live, other players use the [Enter a tournament](/guides/enter-tournament) guide (`depositAndEnterTournament`, claims). That path deposits against an existing tournament id; creator create deposits before the tournament exists, using `clientTournamentId`. ## Related [#related] * [Enter a tournament](/guides/enter-tournament): player deposit, chip picks on matches, claims * [Authentication](/concepts/authentication): Bearer JWT * [Common patterns](/reference/common-patterns): paid-create errors * [SDK API reference](/reference/sdk-api): public / user / tournaments methods # Enter a tournament (/guides/enter-tournament) **By the end:** you'll have deposited into a tournament, entered, placed chip picks on match markets, and know how claims work. ## Prerequisites [#prerequisites] * `tournamentsBaseUrl` and `tournamentsAuth` (JWT from markets `eoaLogin`) * Funded managed account for the buy-in (testnet: [auto-mint faucet](/concepts/money#testnet-faucet-register-does-not-fund-you)) * Optional `onchain` for vault deposit ## HTTP enter (with tx hash) [#http-enter-with-tx-hash] ```ts const tournament = await sdk.tournaments!.tournaments.getById('tournament-id'); // After on-chain vault deposit for this tournament id await sdk.tournaments!.tournaments.enter('tournament-id', { depositTxHash: '0x…', stakeAsset: 'credits', // or 'usdc' }); ``` ## On-chain deposit + enter [#on-chain-deposit--enter] ```ts import { waitForTournamentEntry } from '@bento.fun/sdk'; const res = await sdk.onchain!.depositAndEnterTournament( 'tournament-id', { amountUsdc: '100' }, // body object, not a bare string ); if (!res.success) throw new Error(res.error); // `walletAddress` is the wallet you deposited from — my-status is keyed by wallet await waitForTournamentEntry(() => sdk.tournaments!.tournaments.getMyStatus('tournament-id', walletAddress), ); ``` Confirm entry and your chip budget: ```ts const status = await sdk.tournaments!.tournaments.getMyStatus('tournament-id', walletAddress); // status.hasEntry === true // status.chipsSummary — e.g. { startChips: 1000, chipsStaked, chipsRemaining, … } ``` ## After enter: bet on matches with chips [#after-enter-bet-on-matches-with-chips] Entering spends **credits/USDC** into the tournament vault (the buy-in). Playing the bracket uses a separate, **virtual chip** balance per stage (typically **1000 chips** when the stage starts). Chips are not USDC and are not placed with `sdk.user.placeBet`. Use `submitPicks` on a **LIVE** stage that has **OPEN** match markets: ```ts const detail = await sdk.tournaments!.tournaments.getById(tournamentId); const stage = detail.stages.find((s) => s.stageIndex === 1); // or current LIVE stage const stageId = stage.id; const markets = stage.markets; // need OPEN markets on the stage // Default chip rules (tournament config can override): // - max ~50 chips per pick // - min ~60 chips total staked across the request // So you usually need at least two markets (e.g. 30 + 30). await sdk.tournaments!.tournaments.submitPicks(tournamentId, stageId, { picks: [ { marketId: markets[0].id, side: 0, stakeChips: 30 }, { marketId: markets[1].id, side: 0, stakeChips: 30 }, ], }); const { picks, chipsSummary } = await sdk.tournaments!.tournaments.getPicks(tournamentId, stageId); ``` | Concept | Detail | | ------------------- | -------------------------------------------------------- | | **Buy-in** | Credits/USDC vault deposit + `enter` | | **Chips** | In-tournament stake units for match picks (`stakeChips`) | | **API** | `submitPicks` / `getPicks` / `deletePick` | | **Stage** | Must be `LIVE`; markets must be `OPEN` | | **Not the same as** | Markets-host `placeBet` on a duel | If the stage has no markets yet, the creator must add them before players can pick. Picks fail with structured errors such as `STAGE_NOT_LIVE`, `TOTAL_STAKE_TOO_LOW`, or `STAKE_TOO_HIGH`. ## Claims [#claims] ```ts // Claim via the orchestrator (getClaimProof -> on-chain claim -> confirmClaim): await sdk.onchain!.claimTournamentPayout('tournament-id'); // There is no tournaments.claim(); the explicit REST path is getClaimProof + confirmClaim: // const proof = await sdk.tournaments!.tournaments.getClaimProof('tournament-id'); // ...submit the vault claim tx... then sdk.tournaments!.tournaments.confirmClaim('tournament-id', { wallet, txHash }); ``` ## Related [#related] * [Create a tournament](/guides/create-tournament): creator eligibility + vault deposit * [Money](/concepts/money): faucet + collateral vs tournament chips * [TypeScript SDK](/typescript-sdk): `tournaments`, `onchain` * [Mutation semantics](/concepts/mutations) # Place a bet (/guides/place-bet) **By the end:** you'll have estimated a bet, placed it with `placeBetFromEstimate`, and read your position back. ## Prerequisites [#prerequisites] * Markets host + Builder API key — [Environments](/concepts/environments) * Bearer JWT on `createBentoSdk` — [Authentication](/concepts/authentication) * Funded **managed** account (testnet: [auto-mint faucet](/concepts/money#testnet-faucet-register-does-not-fund-you)) * For **private** markets: membership via `duelInvitations.userJoin` before betting — [Create a market](/guides/create-market#private-markets-invite--join--bet) ## Flow [#flow] ```ts import { createBentoSdk, 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!, auth: walletAuthProvider(() => ({ Authorization: `Bearer ${token}` })), }); const duelId = 'your-duel-id'; // from listDuels row.duelId — NOT row.id const market = await sdk.public.getDuelById({ duelId }); // 1. Estimate (optionIndex is 0|1; amount is collateral wei) // Platform minimum bet is **5** collateral units, in the collateral's decimals // (18 for credits / BSC USDC, 6 for Base USDC). // `estimateBuy` does not enforce the floor: a sub-5 amount can quote then 500 at place. const stake = '10000000000000000000'; // 10 units const est = await sdk.user.bets.estimateBuy({ duelId, optionIndex: 0, betAmountUsdc: stake, slippageBps: 100, }); if (!est.success) throw new Error('estimate rejected'); // narrows to success variant // 2. Place (HTTP acceptance). Prefer `placeBetFromEstimate`: it maps the quote // and derives the slippage floor from `sharesOut`. Do NOT hand-build from // `estimate.min_shares_out` — on larger bets the engine returns it ABOVE // `sharesOut`, emptying the slippage window ("Slippage exceeded"). // `bet` is the option label text (prediction often YES/NO; versus uses optionA/optionB text). await sdk.user.placeBetFromEstimate( { estimate: est.estimate, duelId, duelType: market.duelType ?? 'PREDICTION', // or 'VERSUS' bet: market.options?.[0] ?? 'YES', optionIndex: 0, betAmount: stake, betAmountUsdc: stake, slippageBps: 100, tokenDecimals: 18, }, { idempotencyKey: crypto.randomUUID() }, // stable per attempt, not Date.now() ); // 3. Reconcile — `address` is the managed account from login, not your EOA const shares = await sdk.user.bets.getUserShares({ duelId, address: managedAccountAddress, }); ``` A write returns when the server **accepted** it, not when the chain settled. Poll reads to confirm. → [Acceptance vs finality](/concepts/mutations) ## Related [#related] * [Quickstart](/quickstart): full zero → bet script * [Create a market](/guides/create-market): public create, private invite/join * [Cookbook](/reference/cookbook): method shortlist # Sportmonks sports data (/guides/sportmonks-proxy) **By the end:** you can call Bento’s Sportmonks proxies safely (cached + rate-limited) instead of hitting Sportmonks directly. These routes live on the **tournaments host** (`PARLAY_TOURNAMENT_URL`). They are **not** on `createBentoSdk().tournaments` today (proxy namespaces were trimmed from the public SDK surface). Use `fetch` (or any HTTP client) against the host origin. > Do **not** put a Sportmonks `api_token` in client code. The server injects it. If you send one, it is stripped. ## Setup [#setup] ```bash export PARLAY_TOURNAMENT_URL='https://bento-fun-tournaments-backend-3nku.onrender.com' export BENTO_BUILDER_API_KEY='bnt_…' # same key as markets / createBentoSdk ``` → [Environments](/concepts/environments) ```ts const base = process.env.PARLAY_TOURNAMENT_URL!; // no trailing slash ``` Catalog / betting still use the markets host. Sports **data** uses this tournaments-host proxy. ## Prefer the right TTL (this is the rate limit) [#prefer-the-right-ttl-this-is-the-rate-limit] Every proxy call should pass a `ttl` profile. The backend uses **stale-while-revalidate**: fresh cache is served without upstream; stale can be served while refreshing; identical in-flight requests are single-flighted. | `ttl` | Fresh | Stale window | Use for | | --------- | ----- | ------------ | ----------------------------------------------- | | `live` | 30s | 5 min | Live scores / livescores only (see clamp below) | | `ended` | 60s | 24h | Finished fixtures / final stats | | `news` | 5 min | 1h | News-style payloads (v3 only) | | `logo` | 24h | 7d | Static images / logos | | `default` | 60s | 30 min | Schedules, squads, standings | **Server TTL clamp:** `ttl: 'live'` is honored only on live-ish paths (`livescores`, `fixtures/latest`, `inplay`, paths containing `/live/`). On other paths the server forces `default` so clients can’t short-circuit the cache. **Client rules (so you don’t burn the shared Sportmonks quota):** 1. Always set `ttl` — don’t rely on default for live data. 2. Poll **no faster than the fresh window** (e.g. live ≥ 30s). 3. Reuse responses; check `_meta.fromCache` / `_meta.isStale` instead of re-requesting. 4. Prefer the typed GET helpers below for common football needs (they’re already TTL-cached server-side). 5. Use **GET only** on the v3 proxy (`method: 'POST'` is rejected). ### Backend protections (automatic) [#backend-protections-automatic] You don’t configure these — they protect the shared upstream **and** the public surface: | Layer | Limit | | --------------------------- | --------------------------------------------------------------- | | Per-IP proxy POST | **60 req/min** on `/bento/sportmonks/proxy` + cricket-v2 | | Per-IP football GET helpers | **120 req/min** | | Host-wide floor | \~300 req/min/IP | | Per-cache-key concurrency | 8 (overflow → stale) | | Global upstream gate | \~12 concurrent sports upstream calls | | Circuit breaker | 5× HTTP 429 in 30s → path open 60s (stale only) | | Path allowlist | `football/` \| `cricket/` \| `motorsport/` (+ cricket-v2 roots) | If you see `429` with `Retry-After`, back off. During a Sportmonks 429 storm, responses may stay stale on purpose. ## Routes [#routes] ### 1. Sportmonks v3 proxy (football / cricket path roots / motorsport) [#1-sportmonks-v3-proxy-football--cricket-path-roots--motorsport] `POST ${PARLAY_TOURNAMENT_URL}/bento/sportmonks/proxy` ```ts const res = await fetch(`${base}/bento/sportmonks/proxy`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-builder-api-key': process.env.BENTO_BUILDER_API_KEY!, }, body: JSON.stringify({ method: 'GET', // default GET // Must start with football/ | cricket/ | motorsport/ path: 'football/fixtures/date/2026-08-05', params: { include: 'participants' }, // api_token stripped if present ttl: 'default', }), }); const body = (await res.json()) as { data: unknown; // upstream Sportmonks JSON (usually { data: ... }) _meta?: { fromCache: boolean; isStale: boolean; fetchedAt: number; cacheKey: string }; }; // Most clients use body.data as if it came from Sportmonks directly console.log(body.data, body._meta?.fromCache); ``` | Body field | Notes | | ---------- | ---------------------------------------------------------------------------------------------- | | `path` | Relative Sportmonks v3 path, e.g. `football/leagues`, `motorsport/stages`. Regex `^[\w\-/]+$`. | | `params` | Query params forwarded upstream (never send `api_token`). | | `ttl` | `live` \| `ended` \| `logo` \| `news` \| `default` (`live` clamped to live paths) | | `method` | **GET only** (`POST` → 400) | ### 2. Cricket v2 proxy (separate Sportmonks host) [#2-cricket-v2-proxy-separate-sportmonks-host] `POST ${PARLAY_TOURNAMENT_URL}/bento/sportmonks/cricket-v2/proxy` Cricket v2 is **not** on the v3 host. Same envelope + similar TTL / circuit-breaker behavior. Allowed path roots include `fixtures`, `livescores`, `teams`, `seasons`, `squad`, etc. ```ts const res = await fetch(`${base}/bento/sportmonks/cricket-v2/proxy`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-builder-api-key': process.env.BENTO_BUILDER_API_KEY!, }, body: JSON.stringify({ path: 'livescores', params: {}, ttl: 'live', }), }); const { data, _meta } = await res.json(); ``` ### 3. Cached football helpers (prefer these when they fit) [#3-cached-football-helpers-prefer-these-when-they-fit] | Method | Path | | ----------------- | ------------------------------------------------------------ | | Fixture snapshot | `GET /bento/sportmonks/football/fixture/:fixtureId/snapshot` | | Team squad | `GET /bento/sportmonks/football/team/:teamId/squad` | | Team season stats | `GET /bento/sportmonks/football/team/:teamId/season-stats` | ```ts const builderHeaders = { 'x-builder-api-key': process.env.BENTO_BUILDER_API_KEY!, }; const snapshot = await fetch( `${base}/bento/sportmonks/football/fixture/${fixtureId}/snapshot`, { headers: builderHeaders }, ).then((r) => r.json()); const squad = await fetch( `${base}/bento/sportmonks/football/team/${teamId}/squad`, { headers: builderHeaders }, ).then((r) => r.json()); ``` These are server-cached aggregations — better than inventing equivalent v3 proxy paths for the same data. ## Auth [#auth] **Builder API key required** — send the same `x-builder-api-key` you use on the markets host (`BENTO_BUILDER_API_KEY` / `createBentoSdk({ apiKey })`). [Mint a key](/concepts/builder-api-key) once on markets — tournaments validates against the same `BuilderApp` store (no separate registration). No **user JWT** required for these Sportmonks proxy reads (the server holds the Sportmonks token). Use the tournaments host URL from [Environments](/concepts/environments). | Layer | Header | Purpose | | --------------- | ------------------------- | -------------------------------- | | Builder (app) | `x-builder-api-key` | Which app is calling | | User (optional) | `Authorization: Bearer …` | Not needed for sports data reads | Do not confuse this with markets `sdk.user` (Builder key + Bearer JWT for bets). ## What not to do [#what-not-to-do] | Don’t | Do instead | | -------------------------------------------------------- | ------------------------------------------------------- | | Call `api.sportmonks.com` with your own key from the app | Call Bento’s proxy | | Poll live every 1–5s | `ttl: 'live'` and poll ≥ 30s | | Omit `ttl` on hot paths | Set `live` / `ended` / `default` explicitly | | Forward arbitrary paths | Stick to allowlisted roots | | Omit `x-builder-api-key` | Send `BENTO_BUILDER_API_KEY` on every sports proxy call | | Treat proxy as a betting API | Betting stays on markets host / tournament chips | ## Related [#related] * [Environments](/concepts/environments) — `PARLAY_TOURNAMENT_URL` * [Cookbook](/reference/cookbook) — method shortlist (markets + tournaments) * [OpenAPI specs](/reference/openapi) — `tournaments.json` includes these paths * Other sports proxies on the same host (NFL/MLB/NBA/OpenF1/Odds) follow the same “cache + gate” idea; Sportmonks is the primary documented surface for soccer/cricket/motorsport data # Test the SDK (/guides/test-the-sdk) Use the [sdk-sandbox](https://github.com/Bentodotfun/sdk-sandbox) repo, a standalone project that installs `@bento.fun/sdk` from npm rather than a local link. This catches publish issues, broken exports, and live API mismatches early. **By the end:** you'll have run the published SDK against the live API in a clean project. ## Setup [#setup] ```bash git clone https://github.com/Bentodotfun/sdk-sandbox.git cd sdk-sandbox cp .env.example .env # BENTO_URL=https://internal-server.bento.fun # BENTO_BUILDER_API_KEY=bnt_… # mint at /concepts/builder-api-key # PARLAY_TOURNAMENT_URL=https://bento-fun-tournaments-backend-3nku.onrender.com # optional npm install npm run test:import # start here: no network npm test # full live public API checks ``` ## What `npm test` runs [#what-npm-test-runs] | Step | Checks | | ---------------------- | ----------------------------------------------------------- | | **import-smoke** | Package imports, `createBentoSdk()` factory | | **markets-public** | `listDuels`, `protocolStats`, `getDuelById` (with `duelId`) | | **tournaments-public** | `tournaments.list` | Individual scripts: ```bash npm run test:import npm run test:markets npm run test:tournaments ``` ## Environment [#environment] | Variable | Purpose | | ----------------------- | ------------------------------------------------------------- | | `BENTO_URL` | Markets host (`https://internal-server.bento.fun`) | | `BENTO_BUILDER_API_KEY` | Builder API key (`createBentoSdk` requires it) | | `PARLAY_TOURNAMENT_URL` | Tournaments host (see [Environments](/concepts/environments)) | Omit `PARLAY_TOURNAMENT_URL` to skip tournaments checks. ## When to run [#when-to-run] * After publishing a new `@bento.fun/sdk` version to npm * Before updating docs examples or integration guides * When debugging “works locally but not from npm” * Onboarding new developers or interns testing the SDK ## Where a mismatch comes from [#where-a-mismatch-comes-from] | Symptom | What it means, and what to do | | --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | | A route or method the SDK calls is wrong or missing | An SDK bug. Pin your last working `@bento.fun/sdk` version, report it to the Bento team; the fix ships in a new release. | | A code example in these docs is wrong | A docs bug. Report it; check the [changelog](/reference/changelog) for a corrected pattern. | | A gap in your test coverage | Add a case to your [sdk-sandbox](https://github.com/Bentodotfun/sdk-sandbox) clone. | | A response shape or field changed | Upgrade to the latest `@bento.fun/sdk`; its types track the API. See the [changelog](/reference/changelog). | | A response shape you did not expect | Confirm you are on the latest SDK, then see [common patterns](/reference/common-patterns) (`duelId`, wrapped responses, auth). | ## Related [#related] * [sdk-sandbox on GitHub](https://github.com/Bentodotfun/sdk-sandbox) * [Common patterns](/reference/common-patterns): `duelId`, wrapped responses, auth # Changelog (/reference/changelog) Notable changes to `@bento.fun/sdk`, newest first. ## 0.7.0 [#070] * Trimmed the public client surface to the intended integrator API (internal telegram / wallet / sports / proxy namespaces removed) ## 0.6.0 [#060] * **`placeBetFromEstimate`**: quote-to-bet helper * **Tournament enter:** typed request body; credits deposit approve at 18 decimals; wallet injection; `waitForTournamentEntry` resolves on `hasEntry` * **Create market/tournament:** no auto-retry on non-idempotent POSTs; validation field errors; geoblock detection fix * **Agent action:** `accessCode` in body/header (not query string) * **Referral analytics:** typed `ReferralAnalyticsApi`; refreshed mainnet OpenAPI snapshot * Docs: onboarding revamp, create market/tournament guides, Bearer JWT auth alignment ## 0.5.5 [#055] * npm README: Bearer JWT auth for markets (synced with [Authentication](/concepts/authentication)) * Docs: place-parlay auth snippet, Retry-After note for 0.5.4+ * **Referral analytics:** typed SDK interfaces ## 0.5.4 [#054] * **On-chain:** fix double-scaled collateral approval on parlay / LP flows * **Retry/errors:** honor `Retry-After` on 429; preserve `network_error` cause; reconnect JSDoc fix ## 0.5.3 [#053] * `PublicDuelSummary` type JSDoc: `id` vs `duelId`: use **`duelId`** for `getDuelById` * npm README rewritten for integrators; `homepage`, `bugs`, and `keywords` on package.json * Docs site: auto-generated [SDK API reference](/reference/sdk-api), [common patterns](/reference/common-patterns), [test the SDK](/guides/test-the-sdk) guide * Integration sandbox at [github.com/Bentodotfun/sdk-sandbox](https://github.com/Bentodotfun/sdk-sandbox) ## 0.5.2 [#052] * Docs examples use `BENTO_URL` and `PARLAY_TOURNAMENT_URL` env vars * npm README cleanup (docs.bento.fun link only) ## 0.5.1 [#051] * Legacy notification routes (`registerLegacyDevice`, `unregisterLegacyDevice`) * Auth header helpers (`bulkRegisterAuthProvider`, `agentV1AuthProvider`, …) * Tournaments OpenAPI pin + generated types ## 0.5.0 [#050] * Complete integrator surface audit closure * Agent modules, social feeds * Fixed `getContests` path ## 0.4.0 [#040] * On-chain orchestration, feeds/proxies, social layer Install a specific version: ```bash npm install @bento.fun/sdk@0.7.0 ``` # Common patterns & pitfalls (/reference/common-patterns) Answers to questions that come up when wiring the SDK against live APIs. For the happy path, start with [Quickstart](/quickstart). ## Two hosts, one factory [#two-hosts-one-factory] ```ts 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 }), }); ``` | Client | Host env | Typical auth | | ------------------------- | ----------------------- | ------------------------ | | `sdk.public` / `sdk.user` | `BENTO_URL` | Bearer JWT on `user` | | `sdk.tournaments` | `PARLAY_TOURNAMENT_URL` | JWT on `tournamentsAuth` | → [Environments](/concepts/environments) · [Two API hosts](/concepts/two-hosts) ## `id` vs `duelId` on market rows [#id-vs-duelid-on-market-rows] | Field | Use for | | -------- | -------------------------------------------------------- | | `id` | Database row id — **do not** pass to `getDuelById` | | `duelId` | On-chain duel id — **use this** for detail, bets, charts | ```ts const { data } = await sdk.public.listDuels({ page: 1, limit: 10 }); await sdk.public.getDuelById({ duelId: data[0].duelId }); // ✓ // await sdk.public.getDuelById({ duelId: data[0].id }); // ✗ 404 ``` ## Amounts are in wei [#amounts-are-in-wei] `betAmountUsdc` / `betAmount` are **base units** (18 decimals on BSC / credits). `'10'` is 10 wei. Prefer `parseUnits('10', 18)` or a literal like `'10000000000000000000'`. ## Collateral: credits vs USDC [#collateral-credits-vs-usdc] | Mode | Geo | | --------- | --------------------------------- | | `usdc` | Geo-gated (`403` + `GEO_BLOCKED`) | | `credits` | Not geo-gated | Fund the managed account before writing. Testnet: `await sdk.public.faucet.mint({ address: managedAccountAddress })`. → [Money](/concepts/money) ## Place bets with `placeBetFromEstimate` [#place-bets-with-placebetfromestimate] ```ts const est = await sdk.user.bets.estimateBuy({ duelId, optionIndex: 0, betAmountUsdc: stake, slippageBps: 100, }); if (!est.success) throw new Error('estimate rejected'); await sdk.user.placeBetFromEstimate( { estimate: est.estimate, duelId, duelType: market.duelType ?? 'PREDICTION', bet: market.options?.[0] ?? 'YES', optionIndex: 0, betAmount: stake, betAmountUsdc: stake, slippageBps: 100, tokenDecimals: 18, }, { idempotencyKey }, // caller-stable, not Date.now() ); ``` ## Wrapped API responses [#wrapped-api-responses] Some tournaments endpoints return **objects**, not bare arrays: ```ts const { tournaments, total } = (await sdk.tournaments!.tournaments.list({ status: 'active', })) as { tournaments: unknown[]; total: number }; ``` Schemas: [OpenAPI specs](/reference/openapi). ## HTTP acceptance ≠ on-chain finality [#http-acceptance--on-chain-finality] `placeBet` / `placeBetFromEstimate`, `createDuel`, invite `userJoin`, pack picks, and tournament enters return when the server **accepted** the request. Poll reads afterward. → [Mutations](/concepts/mutations) ## Errors [#errors] Failed calls throw a `BentoSdkErrorException`. **Inspect with the exported helpers**, which take the value you caught and unwrap `.sdkError` for you: ```ts import { isAuthError, isValidationError, isRateLimited, isGeoblockError, getFieldErrors, getRetryAfterMs, getRequestId, getStatus, getErrorCode, } from '@bento.fun/sdk'; try { await sdk.user.placeBetFromEstimate(input, { idempotencyKey }); } catch (err) { if (isRateLimited(err)) { await sleep(getRetryAfterMs(err) ?? 1_000); } else if (isValidationError(err)) { showFieldErrors(getFieldErrors(err)); } else if (isGeoblockError(err)) { // 403 GEO_BLOCKED on the usdc stack — try collateralMode: 'credits' } else if (isAuthError(err)) { await refreshSession(); } else { report({ status: getStatus(err), code: getErrorCode(err), requestId: getRequestId(err), }); } } ``` Branch on `getErrorCode(err)` rather than matching `message`; messages are prose and will change. `getRequestId(err)` returns the id the **server** echoed on `x-request-id` when it sends one, falling back to the client-generated id. Quote it in bug reports. Do **not** call `isBentoSdkError(caught)`. It narrows the error *record*, and what is thrown is the *exception* that carries that record on `.sdkError`, so it is always `false`. Use the helpers above (or `toSdkError(err)` for the raw record). ## Error kinds [#error-kinds] | `kind` | HTTP | Fields present | Helper | | ------------------ | ------------- | -------------------------------------------------------- | ------------------------------------- | | `http_error` | 404, 451, 5xx | `status`, `code?`, `correlationId`, `message`, `details` | `getStatus`, `getErrorCode` | | `auth_error` | 401, 403 | `message`, `status`, `code?` | `isAuthError` | | `validation_error` | 400, 422 | `status`, `fieldErrors?`, `message` | `isValidationError`, `getFieldErrors` | | `rate_limited` | 429 | `status`, `retryAfterMs?`, `code?`, `message` | `isRateLimited`, `getRetryAfterMs` | | `timeout` | n/a | `timeoutMs`, `message` | `isTimeoutError` | | `network_error` | n/a | `retryable`, `cause?`, `message` | `isNetworkError` | Every kind also carries `requestId`; read it with `getRequestId`. ## Common errors [#common-errors] | You see | Cause | Fix | | ---------------------------------------------------- | -------------------------------------------------- | ------------------------------------------------------------ | | `401` / `403` (`auth_error`) on `sdk.user` | missing/invalid Bearer JWT | `eoaLogin` / register → `Authorization: Bearer` | | `400` Insufficient Credits | managed account unfunded | testnet auto-mint on managed address | | `400` Start time must be in the future | `startTime` ≤ now | set strictly ahead | | `500` Pre-flight simulation failed | on-chain create reverted (often short `startTime`) | larger offset (\~31 min public / \~5 min private) | | `500` Unable to place bet… after estimate | stake \< **5** units | stake ≥ 5; pass `tokenDecimals` | | `400` Paid tournaments require `clientTournamentId`… | stage-1 buy-in without vault deposit | approve + vault deposit → create with `creatorDepositTxHash` | | `404` from `getDuelById` | passed database `id` | use `duelId` | | `403` `GEO_BLOCKED` | USDC geo gate | `isGeoblockError`; use `credits` | | `429` (`rate_limited`) | builder-key budget exhausted | wait `getRetryAfterMs(err)`; configure SDK `rateLimit` | | `sdk.onchain.*` `{ success: false }` | on-chain helpers never throw | branch on `result.success` | ## Rate limits [#rate-limits] **Limits are enforced per builder key, not per endpoint or per user.** Default is 120/min for team-issued keys; self-serve testnet keys get 300/min. Because the budget is shared, a burst on one endpoint returns 429 for everything that key is doing. A 429 surfaces as `kind: 'rate_limited'`. The SDK parses `Retry-After` into `retryAfterMs` and honours it in automatic retry backoff: ```ts if (isRateLimited(err)) await sleep(getRetryAfterMs(err) ?? 1_000); ``` ### Pacing calls before you hit the limit [#pacing-calls-before-you-hit-the-limit] Opt-in client pacing (off unless configured): ```ts const sdk = createBentoSdk({ baseUrl, apiKey, auth, rateLimit: { default: { limit: 120, intervalMs: 60_000 }, endpoints: { 'POST /bento/user/bets/create': { limit: 20, intervalMs: 60_000 }, '/bento/user': { limit: 60, intervalMs: 60_000 }, }, onLimit: 'wait', // or 'throw' to shed load maxWaitMs: 60_000, }, }); ``` Rules match most-specific-first: `METHOD /exact/path`, then `/exact/path`, then longest path prefix, then `default`. Paths work with or without the `/bento` prefix. With `onLimit: 'throw'` the SDK fails fast with `code: 'client_rate_limit'` and does **not** retry it. Use `getErrorCode(err)` to tell a local refusal from a real server 429. This is client-side politeness, not enforcement. The server remains the authority. → [Builder API key](/concepts/builder-api-key) ## Security [#security] * Never hard-code private keys or Builder API keys * Markets `sdk.user` auth is Bearer JWT, not raw `x-wallet-*` * `credits` bypasses geo; `usdc` does not ## Where to look next [#where-to-look-next] | Need | Page | | ---------------- | --------------------------------------- | | Golden path | [Quickstart](/quickstart) | | Method shortlist | [Cookbook](/reference/cookbook) | | Every SDK method | [SDK API reference](/reference/sdk-api) | | JSON schemas | [OpenAPI specs](/reference/openapi) | | npm smoke tests | [Test the SDK](/guides/test-the-sdk) | # SDK cookbook (/reference/cookbook) Use this page as the default lookup. For every method + route, see the [SDK API reference](/reference/sdk-api). Factory always needs `apiKey` + `auth`. Markets user calls also need a Bearer JWT. → [Quickstart](/quickstart) ## Setup (canonical) [#setup-canonical] ```ts 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 [#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 [#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](/concepts/authentication) ## Markets — trade [#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](/guides/place-bet) ## Testnet faucet [#testnet-faucet] ```ts await sdk.public.faucet.mint({ address: managedAccountAddress }); // ~1000 USDC + ~1000 credits on the managed account (testnet) ``` ## Tournaments (when `tournamentsBaseUrl` is set) [#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](/guides/enter-tournament) ## Sports data (Sportmonks proxy) [#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`) | ```ts const sportsHeaders = { 'Content-Type': 'application/json', 'x-builder-api-key': process.env.BENTO_BUILDER_API_KEY!, }; ``` → [Sportmonks sports data](/guides/sportmonks-proxy) (TTL table + circuit breaker) ## Errors (quick) [#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) | → [Common patterns](/reference/common-patterns) ## Intentionally out of scope here [#intentionally-out-of-scope-here] * Full F1 / Polymarket / packs surface — see [SDK API reference](/reference/sdk-api) * Request JSON schemas — [OpenAPI specs](/reference/openapi) # OpenAPI specs (/reference/openapi) Bento publishes **pinned OpenAPI JSON** snapshots so agents, LLMs, codegen, and integrators can fetch stable API schemas without an interactive Swagger UI. ## Downloads [#downloads] | API | URL | | ----------- | ------------------------------------------------------ | | Markets | [/openapi/markets.json](/openapi/markets.json) | | Tournaments | [/openapi/tournaments.json](/openapi/tournaments.json) | These files are copied from the pinned snapshots in the documentation package at build time (`pnpm run sync:public`). **Admin / protocol-admin paths are stripped** before publish. They reflect the versions bundled with the docs, not necessarily the latest live backend. ## How to use them [#how-to-use-them] * **Agents & LLMs** — fetch raw JSON for request/response shapes; pair with [SDK API reference](/reference/sdk-api) for method names and auth. * **Type generation** — point OpenAPI codegen at the JSON URL or a local copy after `sync:public`. * **Not a live explorer** — there is no embedded Scalar/Swagger UI on this site. Use the JSON as a schema, or call the SDK / HTTP APIs directly. For SDK method names and auth patterns, see [Common patterns](/reference/common-patterns) and [Authentication](/concepts/authentication). # Overview (/reference/sdk-api) {/* AUTO-GENERATED by packages/documentation/scripts/generate-sdk-reference.mjs, do not edit by hand */} Every HTTP method exposed by `@bento.fun/sdk`, grouped in the left nav under **SDK API reference**. Prefer the [Cookbook](/reference/cookbook) for day-to-day work. Use [OpenAPI specs](/reference/openapi) for request/response JSON schemas (agents / LLMs / codegen). **252 methods** across 31 namespaces. > **New here?** Start with [Quickstart](/quickstart), then [Cookbook](/reference/cookbook) and [Common patterns](/reference/common-patterns) (`duelId` vs `id`, wei, auth). ## Setup [#setup] ```ts import { createBentoSdk, walletAuthProvider, jwtAuthProvider } 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 }), }); ``` ## Sections [#sections] # Public (/reference/sdk-api/public) {/* AUTO-GENERATED by packages/documentation/scripts/generate-sdk-reference.mjs, do not edit by hand */} [← SDK API reference](/reference/sdk-api) · Prefer [Cookbook](/reference/cookbook) for common flows. **44 methods** across 10 namespaces. ## Setup [#setup] ```ts import { createBentoSdk, walletAuthProvider, jwtAuthProvider } from '@bento.fun/sdk'; const sdk = createBentoSdk({ baseUrl: process.env.BENTO_URL!, apiKey: process.env.BENTO_BUILDER_API_KEY!, }); ``` ## sdk.public.analytics [#sdkpublicanalytics] Namespace `sdk.public.analytics`, 1 method(s). Example: ```ts await sdk.public.analytics.getPlatformReport(/* args */); ``` | Method | HTTP | Path | Auth | | ------------------- | ---- | --------------------------- | ------ | | `getPlatformReport` | GET | `/bento/analytics/platform` | Public | ## sdk.public.auth [#sdkpublicauth] Namespace `sdk.public.auth`, 8 method(s). Example: ```ts await sdk.public.auth.auth0Login({ /* body */ }); ``` | Method | HTTP | Path | Auth | | -------------------- | ---- | --------------------------------- | -------- | | `auth0Login` | POST | `/bento/user/auth/auth0/login` | Public\* | | `auth0Register` | POST | `/bento/user/auth/auth0/register` | Public\* | | `checkUsername` | GET | `/bento/user/auth/check-username` | Public | | `eoaChallenge` | POST | `/bento/user/auth/eoa/challenge` | Public\* | | `eoaLogin` | POST | `/bento/user/auth/eoa/login` | Public\* | | `eoaRegister` | POST | `/bento/user/auth/eoa/register` | Public\* | | `loginOrRegister` | POST | `/bento/user/auth/eoa/challenge` | Public\* | | `uploadProfileImage` | POST | `/bento/user/auth/profile-image` | Public\* | ### Mutation snippets [#mutation-snippets] First-party only — third-party builders must use EOA. **`sdk.public.auth.auth0Login`**, `POST /bento/user/auth/auth0/login` ```ts await sdk.public.auth.auth0Login({ /* body */ }); ``` First-party only — third-party builders must use EOA. **`sdk.public.auth.auth0Register`**, `POST /bento/user/auth/auth0/register` ```ts await sdk.public.auth.auth0Register({ /* body */ }); ``` Server-built SIWE message bound to this builder key + domain. **`sdk.public.auth.eoaChallenge`**, `POST /bento/user/auth/eoa/challenge` ```ts await sdk.public.auth.eoaChallenge({ /* body */ }); ``` **`sdk.public.auth.eoaLogin`**, `POST /bento/user/auth/eoa/login` ```ts await sdk.public.auth.eoaLogin({ /* body */ }); ``` **`sdk.public.auth.eoaRegister`**, `POST /bento/user/auth/eoa/register` ```ts await sdk.public.auth.eoaRegister({ /* body */ }); ``` One call: sign once, then log the wallet in or register it (with the same signature) if the backend has never seen it. Returns the JWT — pass it to `sdk.withAuth(token)`. Provide `username` to allow registering a new wallet. Prefer `eoaChallenge` + explicit login/register when you need the first-party warning baked into the signed message. **`sdk.public.auth.loginOrRegister`**, `POST /bento/user/auth/eoa/challenge` ```ts await sdk.public.auth.loginOrRegister({ /* body */ }); ``` Multipart profile image upload (`image` field). Requires wallet auth. **`sdk.public.auth.uploadProfileImage`**, `POST /bento/user/auth/profile-image` ```ts await sdk.public.auth.uploadProfileImage({ /* body */ }); ``` ## sdk.public.duels [#sdkpublicduels] Namespace `sdk.public.duels`, 3 method(s). Example: ```ts await sdk.public.duels.getById(/* args */); ``` | Method | HTTP | Path | Auth | | ------------- | ---- | -------------------------------------------- | ------ | | `getById` | GET | `/bento/public/duels/get-duel-by-id/:duelId` | Public | | `getContests` | GET | `/bento/user/duels/contest/:duelId` | Public | | `list` | GET | `/bento/public/duels/all` | Public | ## sdk.public.faucet [#sdkpublicfaucet] Namespace `sdk.public.faucet`, 1 method(s). Example: ```ts await sdk.public.faucet.mint({ /* body */ }); ``` | Method | HTTP | Path | Auth | | ------ | ---- | ----------------------- | -------- | | `mint` | POST | `/bento/auto-mint/mint` | Public\* | ### Mutation snippets [#mutation-snippets-1] Mint faucet funds to a wallet (testnet: 1000 USDC + 1000 Credits; mainnet: 1000 Credits, where enabled). Resolves with the mint result — including any `usdc`/`credits` txHashes — or throws if the backend reports the mint failed. Rate-limited to 5/min by the backend; a mainnet mint is 403 unless the host enables it, and an unconfigured faucet is 503 — both surface as thrown SDK errors from the HTTP layer. **`sdk.public.faucet.mint`**, `POST /bento/auto-mint/mint` ```ts await sdk.public.faucet.mint({ /* body */ }); ``` ## sdk.public.leaderboard [#sdkpublicleaderboard] Namespace `sdk.public.leaderboard`, 8 method(s). Example: ```ts await sdk.public.leaderboard.getCreatorsCount(/* args */); ``` | Method | HTTP | Path | Auth | | ---------------------- | ---- | --------------------------------------- | ------ | | `getCreatorsCount` | GET | `/bento/leaderboard/creators/count` | Public | | `getCreatorsPnl` | GET | `/bento/leaderboard/creators/pnl` | Public | | `getGlobalAggregate` | GET | `/bento/leaderboard/global-aggregate` | Public | | `getParticipantsChart` | GET | `/bento/leaderboard/chart/participants` | Public | | `getTradersPnl` | GET | `/bento/leaderboard/traders/pnl` | Public | | `getVolumeChart` | GET | `/bento/leaderboard/chart/volume` | Public | | `listCreators` | GET | `/bento/leaderboard/creators` | Public | | `listTraders` | GET | `/bento/leaderboard/traders` | Public | ## sdk.public.packs [#sdkpublicpacks] Namespace `sdk.public.packs`, 8 method(s). Example: ```ts await sdk.public.packs.getById(/* args */); ``` | Method | HTTP | Path | Auth | | ------------------ | ---- | --------------------------------------------- | ------ | | `getById` | GET | `/bento/packs/:param` | Public | | `getLeaderboard` | GET | `/bento/packs/:param/leaderboard` | Public | | `getPayoutProof` | GET | `/bento/packs/:param/payout-proof` | Public | | `getPayoutSummary` | GET | `/bento/packs/:param/payout-summary` | Public | | `getPriceHistory` | GET | `/bento/packs/:param/price-history-snapshots` | Public | | `getRefundProof` | GET | `/bento/packs/:param/refund-proof` | Public | | `getRefundSummary` | GET | `/bento/packs/:param/refund-summary` | Public | | `list` | GET | `/bento/packs` | Public | ## sdk.public.parentMarkets [#sdkpublicparentmarkets] Namespace `sdk.public.parentMarkets`, 4 method(s). Example: ```ts await sdk.public.parentMarkets.getById(/* args */); ``` | Method | HTTP | Path | Auth | | ---------------- | ---- | -------------------------------------------- | -------- | | `getById` | GET | `/bento/user/parent-markets/:param` | Public | | `listAccessible` | GET | `/bento/user/parent-markets/accessible` | Public | | `listMembers` | GET | `/bento/user/parent-markets/:param/members` | Public | | `validateInvite` | POST | `/bento/user/parent-markets/validate-invite` | Public\* | ### Mutation snippets [#mutation-snippets-2] **`sdk.public.parentMarkets.validateInvite`**, `POST /bento/user/parent-markets/validate-invite` ```ts await sdk.public.parentMarkets.validateInvite({ /* body */ }); ``` ## sdk.public.protocolStats [#sdkpublicprotocolstats] Namespace `sdk.public.protocolStats`, 3 method(s). Example: ```ts await sdk.public.protocolStats.getStats(/* args */); ``` | Method | HTTP | Path | Auth | | ------------ | ---- | ------------------------------- | -------- | | `getStats` | GET | `/bento/protocol-stats` | Public | | `getSummary` | GET | `/bento/protocol-stats/summary` | Public | | `refresh` | POST | `/bento/protocol-stats/refresh` | Public\* | ### Mutation snippets [#mutation-snippets-3] **`sdk.public.protocolStats.refresh`**, `POST /bento/protocol-stats/refresh` ```ts await sdk.public.protocolStats.refresh({ /* body */ }); ``` ## sdk.public.publicBets [#sdkpublicpublicbets] Namespace `sdk.public.publicBets`, 4 method(s). Example: ```ts await sdk.public.publicBets.estimatedWin({ /* body */ }); ``` | Method | HTTP | Path | Auth | | --------------------------- | ---- | ---------------------------------------------------- | -------- | | `estimatedWin` | POST | `/bento/public/bets/estimated-win` | Public\* | | `estimateSell` | POST | `/bento/public/bets/estimate-sell` | Public\* | | `getSellUnlockLiquidity` | GET | `/bento/public/bets/sell-unlock-liquidity/:param` | Public | | `getYesPercentageSnapshots` | GET | `/bento/public/bets/yes-percentage-snapshots/:param` | Public | ### Mutation snippets [#mutation-snippets-4] **`sdk.public.publicBets.estimatedWin`**, `POST /bento/public/bets/estimated-win` ```ts await sdk.public.publicBets.estimatedWin({ /* body */ }); ``` **`sdk.public.publicBets.estimateSell`**, `POST /bento/public/bets/estimate-sell` ```ts await sdk.public.publicBets.estimateSell({ /* body */ }); ``` ## sdk.public.withdrawalRequests [#sdkpublicwithdrawalrequests] Namespace `sdk.public.withdrawalRequests`, 4 method(s). Example: ```ts await sdk.public.withdrawalRequests.create({ /* body */ }); ``` | Method | HTTP | Path | Auth | | --------------- | ---- | ---------------------------------------------- | -------- | | `create` | POST | `/bento/user/withdrawal-requests` | Public\* | | `getDailyLimit` | GET | `/bento/user/withdrawal-requests/daily-limit` | Public | | `list` | GET | `/bento/user/withdrawal-requests` | Public | | `preValidate` | POST | `/bento/user/withdrawal-requests/pre-validate` | Public\* | ### Mutation snippets [#mutation-snippets-5] **`sdk.public.withdrawalRequests.create`**, `POST /bento/user/withdrawal-requests` ```ts await sdk.public.withdrawalRequests.create({ /* body */ }); ``` **`sdk.public.withdrawalRequests.preValidate`**, `POST /bento/user/withdrawal-requests/pre-validate` ```ts await sdk.public.withdrawalRequests.preValidate({ /* body */ }); ``` # Tournaments (/reference/sdk-api/tournaments) {/* AUTO-GENERATED by packages/documentation/scripts/generate-sdk-reference.mjs, do not edit by hand */} [← SDK API reference](/reference/sdk-api) · Prefer [Cookbook](/reference/cookbook) for common flows. **116 methods** across 5 namespaces. ## Setup [#setup] ```ts import { createBentoSdk, walletAuthProvider, jwtAuthProvider } from '@bento.fun/sdk'; const sdk = createBentoSdk({ baseUrl: process.env.BENTO_URL!, apiKey: process.env.BENTO_BUILDER_API_KEY!, tournamentsBaseUrl: process.env.PARLAY_TOURNAMENT_URL!, tournamentsAuth: jwtAuthProvider({ getAccessToken: () => token }), }); ``` ## sdk.tournaments.auth [#sdktournamentsauth] Namespace `sdk.tournaments.auth`, 2 method(s). Example: ```ts await sdk.tournaments.auth.check(/* args */); ``` | Method | HTTP | Path | Auth | | ------- | ---- | ------------------ | ------------ | | `check` | GET | `/user/auth/check` | Public / JWT | | `login` | POST | `/user/auth/login` | JWT / Wallet | ### Mutation snippets [#mutation-snippets] **`sdk.tournaments.auth.login`**, `POST /user/auth/login` ```ts await sdk.tournaments.auth.login({ /* body */ }); ``` ## sdk.tournaments.f1 [#sdktournamentsf1] Namespace `sdk.tournaments.f1`, 31 method(s). Example: ```ts await sdk.tournaments.f1.create({ /* body */ }); ``` | Method | HTTP | Path | Auth | | -------------------------- | ------ | -------------------------------------------------------------- | ------------ | | `create` | POST | `/api/tournaments/f1/create` | JWT / Wallet | | `deletePrediction` | DELETE | `/api/tournaments/f1/:param/predictions/:param` | JWT / Wallet | | `enter` | POST | `/api/tournaments/f1/:param/enter` | JWT / Wallet | | `getClaimProof` | GET | `/api/tournaments/f1/:param/claim-proof` | Public / JWT | | `getDashboard` | GET | `/api/tournaments/f1/:param/dashboard` | Public / JWT | | `getDashboardExternal` | GET | `/api/tournaments/f1/dashboard/external` | Public / JWT | | `getDashboardExternalFull` | GET | `/api/tournaments/f1/dashboard/external/full` | Public / JWT | | `getEligibility` | GET | `/api/tournaments/f1/:param/eligibility/:param` | Public / JWT | | `getEloLeaderboard` | GET | `/api/tournaments/f1/:param/leaderboard/elo` | Public / JWT | | `getHeatmap` | GET | `/api/tournaments/f1/:param/heatmap/:param` | Public / JWT | | `getMyPayout` | GET | `/api/tournaments/f1/:param/my-payout` | Public / JWT | | `getMyPicks` | GET | `/api/tournaments/f1/:param/user/:param/my-picks` | Public / JWT | | `getPayouts` | GET | `/api/tournaments/f1/:param/payouts` | Public / JWT | | `getRaceAnalytics` | GET | `/api/tournaments/f1/:param/race-analytics/:param` | Public / JWT | | `getResults` | GET | `/api/tournaments/f1/:param/results/:param` | Public / JWT | | `getRound` | GET | `/api/tournaments/f1/:param/rounds/:param` | Public / JWT | | `getRoundMatchups` | GET | `/api/tournaments/f1/:param/rounds/:param/matchups` | Public / JWT | | `getRoundSideBetHeatmap` | GET | `/api/tournaments/f1/:param/rounds/:param/side-bet-heatmap` | Public / JWT | | `getSeasonLeaderboard` | GET | `/api/tournaments/f1/:param/leaderboard/season` | Public / JWT | | `getTournament` | GET | `/api/tournaments/f1/:param` | Public / JWT | | `getUserPredictions` | GET | `/api/tournaments/f1/:param/user/:param/predictions/:param` | Public / JWT | | `getUserStats` | GET | `/api/tournaments/f1/:param/user/:param/stats` | Public / JWT | | `getWeekendLeaderboard` | GET | `/api/tournaments/f1/:param/leaderboard/weekend/:param` | Public / JWT | | `listDrivers` | GET | `/api/tournaments/f1/:param/drivers` | Public / JWT | | `listRounds` | GET | `/api/tournaments/f1/:param/rounds` | Public / JWT | | `listRoundSideBets` | GET | `/api/tournaments/f1/:param/rounds/:param/side-bets` | Public / JWT | | `lockMyPredictions` | POST | `/api/tournaments/f1/:param/events/:param/lock-my-predictions` | JWT / Wallet | | `postRoundSideBets` | POST | `/api/tournaments/f1/:param/rounds/:param/side-bets` | JWT / Wallet | | `predict` | POST | `/api/tournaments/f1/:param/events/:param/predict` | JWT / Wallet | | `reenter` | POST | `/api/tournaments/f1/:param/reenter` | JWT / Wallet | | `updatePrediction` | PUT | `/api/tournaments/f1/:param/predictions/:param` | JWT / Wallet | ### Mutation snippets [#mutation-snippets-1] **`sdk.tournaments.f1.create`**, `POST /api/tournaments/f1/create` ```ts await sdk.tournaments.f1.create({ /* body */ }); ``` **`sdk.tournaments.f1.deletePrediction`**, `DELETE /api/tournaments/f1/:param/predictions/:param` ```ts await sdk.tournaments.f1.deletePrediction({ /* body */ }); ``` **`sdk.tournaments.f1.enter`**, `POST /api/tournaments/f1/:param/enter` ```ts await sdk.tournaments.f1.enter({ /* body */ }); ``` **`sdk.tournaments.f1.lockMyPredictions`**, `POST /api/tournaments/f1/:param/events/:param/lock-my-predictions` ```ts await sdk.tournaments.f1.lockMyPredictions({ /* body */ }); ``` **`sdk.tournaments.f1.postRoundSideBets`**, `POST /api/tournaments/f1/:param/rounds/:param/side-bets` ```ts await sdk.tournaments.f1.postRoundSideBets({ /* body */ }); ``` **`sdk.tournaments.f1.predict`**, `POST /api/tournaments/f1/:param/events/:param/predict` ```ts await sdk.tournaments.f1.predict({ /* body */ }); ``` **`sdk.tournaments.f1.reenter`**, `POST /api/tournaments/f1/:param/reenter` ```ts await sdk.tournaments.f1.reenter({ /* body */ }); ``` **`sdk.tournaments.f1.updatePrediction`**, `PUT /api/tournaments/f1/:param/predictions/:param` ```ts await sdk.tournaments.f1.updatePrediction({ /* body */ }); ``` ## sdk.tournaments.lp [#sdktournamentslp] Namespace `sdk.tournaments.lp`, 8 method(s). Example: ```ts await sdk.tournaments.lp.getApy(/* args */); ``` | Method | HTTP | Path | Auth | | ---------------------- | ---- | -------------------------------- | ------------ | | `getApy` | GET | `/api/lp/apy` | Public / JWT | | `getBettorExposure` | GET | `/api/lp/bettor/:param/exposure` | Public / JWT | | `getCompartments` | GET | `/api/lp/compartments` | Public / JWT | | `getExposureBreakdown` | GET | `/api/lp/exposure-breakdown` | Public / JWT | | `getPool` | GET | `/api/lp/pool` | Public / JWT | | `getPosition` | GET | `/api/lp/position/:param` | Public / JWT | | `quoteAddLiquidity` | POST | `/api/lp/add-liquidity/quote` | JWT / Wallet | | `quoteRemoveLiquidity` | POST | `/api/lp/remove-liquidity/quote` | JWT / Wallet | ### Mutation snippets [#mutation-snippets-2] **`sdk.tournaments.lp.quoteAddLiquidity`**, `POST /api/lp/add-liquidity/quote` ```ts await sdk.tournaments.lp.quoteAddLiquidity({ /* body */ }); ``` **`sdk.tournaments.lp.quoteRemoveLiquidity`**, `POST /api/lp/remove-liquidity/quote` ```ts await sdk.tournaments.lp.quoteRemoveLiquidity({ /* body */ }); ``` ## sdk.tournaments.parlay [#sdktournamentsparlay] Namespace `sdk.tournaments.parlay`, 28 method(s). Example: ```ts await sdk.tournaments.parlay.claim({ /* body */ }); ``` | Method | HTTP | Path | Auth | | ---------------------------- | ---- | ----------------------------------------- | ------------ | | `claim` | POST | `/api/parlay/claim/:param` | JWT / Wallet | | `createQuote` | POST | `/api/parlay/quote` | JWT / Wallet | | `getBettor` | GET | `/api/parlay/bettor/:param` | Public / JWT | | `getEuropeanSaturdayBuilder` | GET | `/api/parlay/builders/european-saturday` | Public / JWT | | `getFeesConfig` | GET | `/api/parlay/fees/config` | Public / JWT | | `getFeesStats` | GET | `/api/parlay/fees/stats` | Public / JWT | | `getHistory` | GET | `/api/parlay/history` | Public / JWT | | `getMarket` | GET | `/api/parlay/markets/:param` | Public / JWT | | `getOdds` | GET | `/api/parlay/odds/:param` | Public / JWT | | `getParlay` | GET | `/api/parlay/:param` | Public / JWT | | `getPlayerProps` | GET | `/api/parlay/markets/:param/player-props` | Public / JWT | | `getQuote` | GET | `/api/parlay/quote/:param` | Public / JWT | | `getSameTeamRunBuilder` | GET | `/api/parlay/builders/same-team-run` | Public / JWT | | `getSgpMenu` | GET | `/api/parlay/markets/:param/sgp-menu` | Public / JWT | | `getStatus` | GET | `/api/parlay/status/:param` | Public / JWT | | `getTicket` | GET | `/api/parlay/ticket/:param` | Public / JWT | | `getTickets` | GET | `/api/parlay/tickets` | Public / JWT | | `getUserNonce` | GET | `/api/parlay/users/:param/nonce` | Public / JWT | | `getUserStats` | GET | `/api/parlay/users/:param/stats` | Public / JWT | | `getWinnings` | GET | `/api/parlay/winnings` | Public / JWT | | `health` | GET | `/api/parlay/health` | Public / JWT | | `healthLive` | GET | `/api/parlay/health/live` | Public / JWT | | `healthReady` | GET | `/api/parlay/health/ready` | Public / JWT | | `listConditions` | GET | `/api/parlay/conditions` | Public / JWT | | `listMarkets` | GET | `/api/parlay/markets` | Public / JWT | | `previewFees` | GET | `/api/parlay/fees/preview/:param` | Public / JWT | | `validateLegs` | POST | `/api/parlay/legs/validate` | JWT / Wallet | | `validateQuote` | POST | `/api/parlay/quote/validate` | JWT / Wallet | ### Mutation snippets [#mutation-snippets-3] **`sdk.tournaments.parlay.claim`**, `POST /api/parlay/claim/:param` ```ts await sdk.tournaments.parlay.claim({ /* body */ }); ``` Create a signed parlay quote. Pads each leg's `conditionId` to bytes32 and requires a real `bettor` (see `prepareParlayQuoteBody`); leg-count/stake problems surface as typed SDK `validation_error`s rather than opaque 400s. **`sdk.tournaments.parlay.createQuote`**, `POST /api/parlay/quote` ```ts await sdk.tournaments.parlay.createQuote({ /* body */ }); ``` **`sdk.tournaments.parlay.validateLegs`**, `POST /api/parlay/legs/validate` ```ts await sdk.tournaments.parlay.validateLegs({ /* body */ }); ``` **`sdk.tournaments.parlay.validateQuote`**, `POST /api/parlay/quote/validate` ```ts await sdk.tournaments.parlay.validateQuote({ /* body */ }); ``` ## sdk.tournaments.tournaments [#sdktournamentstournaments] Namespace `sdk.tournaments.tournaments`, 47 method(s). Example: ```ts await sdk.tournaments.tournaments.confirmClaim({ /* body */ }); ``` | Method | HTTP | Path | Auth | | --------------------------------- | ------ | -------------------------------------------------------------- | ------------ | | `confirmClaim` | POST | `/api/tournaments/:param/confirm-claim` | JWT / Wallet | | `deletePick` | DELETE | `/api/tournaments/:param/picks/:param` | JWT / Wallet | | `depositCreatorBond` | POST | `/api/tournaments/creator/deposit-bond` | JWT / Wallet | | `enter` | POST | `/api/tournaments/:param/enter` | JWT / Wallet | | `estimatePayout` | POST | `/api/tournaments/:param/estimate-payout` | JWT / Wallet | | `fileMarketDispute` | POST | `/api/tournaments/:param/markets/:param/dispute` | JWT / Wallet | | `fileStageDispute` | POST | `/api/tournaments/:param/stages/:param/markets/:param/dispute` | JWT / Wallet | | `getAllDisputes` | GET | `/api/tournaments/:param/disputes` | Public / JWT | | `getBracket` | GET | `/api/tournaments/:param/bracket` | Public / JWT | | `getBracketStatus` | GET | `/api/tournaments/:param/bracket/status` | Public / JWT | | `getById` | GET | `/api/tournaments/:param` | Public / JWT | | `getCancelRequest` | GET | `/api/tournaments/:param/cancel-request` | Public / JWT | | `getClaimProof` | GET | `/api/tournaments/:param/claim-proof` | Public / JWT | | `getCreatorBondRequirement` | GET | `/api/tournaments/creator/bond-requirement` | Public / JWT | | `getCreatorCreateFlowEligibility` | GET | `/api/tournaments/creator/create-flow-eligibility` | Public / JWT | | `getCreatorEligibility` | GET | `/api/tournaments/creator/eligibility` | Public / JWT | | `getCreatorWalletStats` | GET | `/api/tournaments/creator/:param/stats` | Public / JWT | | `getDepositInstructions` | POST | `/api/tournaments/:param/deposit` | JWT / Wallet | | `getDisputeWindows` | GET | `/api/tournaments/:param/dispute-windows` | Public / JWT | | `getEligibility` | GET | `/api/tournaments/:param/eligibility` | Public / JWT | | `getFormatInfo` | GET | `/api/tournaments/:param/format-info` | Public / JWT | | `getGlobalLeaderboard` | GET | `/api/tournaments/global-leaderboard` | Public / JWT | | `getLeaderboard` | GET | `/api/tournaments/:param/leaderboard` | Public / JWT | | `getMarketDisputes` | GET | `/api/tournaments/:param/markets/:param/disputes` | Public / JWT | | `getMarketDisputeWindow` | GET | `/api/tournaments/:param/markets/:param/dispute-window` | Public / JWT | | `getMyStatus` | GET | `/api/tournaments/:param/my-status` | Public / JWT | | `getPayouts` | GET | `/api/tournaments/:param/payouts` | Public / JWT | | `getPayoutStatus` | GET | `/api/tournaments/:param/payout-status` | Public / JWT | | `getPicks` | GET | `/api/tournaments/:param/stages/:param/picks` | Public / JWT | | `getPlatformAggregate` | GET | `/api/tournaments/platform-leaderboard-aggregate` | Public / JWT | | `getPrizePool` | GET | `/api/tournaments/:param/prize-pool` | Public / JWT | | `getRefundProof` | GET | `/api/tournaments/:param/refund-proof` | Public / JWT | | `getStageDisputeableMarkets` | GET | `/api/tournaments/:param/stages/:param/disputeable-markets` | Public / JWT | | `getStageDisputes` | GET | `/api/tournaments/:param/stages/:param/disputes` | Public / JWT | | `getStageDisputeWindow` | GET | `/api/tournaments/:param/stages/:param/dispute-window` | Public / JWT | | `getStageFixtures` | GET | `/api/tournaments/:param/stages/:param/fixtures` | Public / JWT | | `getStageGroups` | GET | `/api/tournaments/:param/stages/:param/groups` | Public / JWT | | `getStageOdds` | GET | `/api/tournaments/:param/stages/:param/odds` | Public / JWT | | `getStageStandings` | GET | `/api/tournaments/:param/stages/:param/standings` | Public / JWT | | `getUserWalletStats` | GET | `/api/tournaments/user/:param/stats` | Public / JWT | | `health` | GET | `/api/tournaments/health` | Public / JWT | | `list` | GET | `/api/tournaments` | Public / JWT | | `listByWallet` | GET | `/bento/tournaments/by-wallet/:param` | Public / JWT | | `submitCancelRequest` | POST | `/api/tournaments/:param/cancel-request` | JWT / Wallet | | `submitPicks` | POST | `/api/tournaments/:param/stages/:param/picks` | JWT / Wallet | | `withdraw` | POST | `/api/tournaments/:param/withdraw` | JWT / Wallet | | `withdrawCreatorBond` | POST | `/api/tournaments/creator/withdraw-bond` | JWT / Wallet | ### Mutation snippets [#mutation-snippets-4] **`sdk.tournaments.tournaments.confirmClaim`**, `POST /api/tournaments/:param/confirm-claim` ```ts await sdk.tournaments.tournaments.confirmClaim({ /* body */ }); ``` **`sdk.tournaments.tournaments.deletePick`**, `DELETE /api/tournaments/:param/picks/:param` ```ts await sdk.tournaments.tournaments.deletePick({ /* body */ }); ``` **`sdk.tournaments.tournaments.depositCreatorBond`**, `POST /api/tournaments/creator/deposit-bond` ```ts await sdk.tournaments.tournaments.depositCreatorBond({ /* body */ }); ``` **`sdk.tournaments.tournaments.enter`**, `POST /api/tournaments/:param/enter` ```ts await sdk.tournaments.tournaments.enter({ /* body */ }); ``` **`sdk.tournaments.tournaments.estimatePayout`**, `POST /api/tournaments/:param/estimate-payout` ```ts await sdk.tournaments.tournaments.estimatePayout({ /* body */ }); ``` **`sdk.tournaments.tournaments.fileMarketDispute`**, `POST /api/tournaments/:param/markets/:param/dispute` ```ts await sdk.tournaments.tournaments.fileMarketDispute({ /* body */ }); ``` **`sdk.tournaments.tournaments.fileStageDispute`**, `POST /api/tournaments/:param/stages/:param/markets/:param/dispute` ```ts await sdk.tournaments.tournaments.fileStageDispute({ /* body */ }); ``` **`sdk.tournaments.tournaments.getDepositInstructions`**, `POST /api/tournaments/:param/deposit` ```ts await sdk.tournaments.tournaments.getDepositInstructions({ /* body */ }); ``` **`sdk.tournaments.tournaments.submitCancelRequest`**, `POST /api/tournaments/:param/cancel-request` ```ts await sdk.tournaments.tournaments.submitCancelRequest({ /* body */ }); ``` **`sdk.tournaments.tournaments.submitPicks`**, `POST /api/tournaments/:param/stages/:param/picks` ```ts await sdk.tournaments.tournaments.submitPicks({ /* body */ }); ``` **`sdk.tournaments.tournaments.withdraw`**, `POST /api/tournaments/:param/withdraw` ```ts await sdk.tournaments.tournaments.withdraw({ /* body */ }); ``` **`sdk.tournaments.tournaments.withdrawCreatorBond`**, `POST /api/tournaments/creator/withdraw-bond` ```ts await sdk.tournaments.tournaments.withdrawCreatorBond({ /* body */ }); ``` # User (wallet) (/reference/sdk-api/user) {/* AUTO-GENERATED by packages/documentation/scripts/generate-sdk-reference.mjs, do not edit by hand */} [← SDK API reference](/reference/sdk-api) · Prefer [Cookbook](/reference/cookbook) for common flows. **92 methods** across 9 namespaces. ## Setup [#setup] ```ts import { createBentoSdk, walletAuthProvider, jwtAuthProvider } from '@bento.fun/sdk'; const sdk = createBentoSdk({ baseUrl: process.env.BENTO_URL!, apiKey: process.env.BENTO_BUILDER_API_KEY!, auth: walletAuthProvider(() => ({ Authorization: `Bearer ${token}` })), }); ``` ## sdk.user.bets [#sdkuserbets] Namespace `sdk.user.bets`, 10 method(s). Example: ```ts await sdk.user.bets.estimateBuy({ /* body */ }); ``` | Method | HTTP | Path | Auth | | --------------------------------- | ---- | ------------------------------------------------------------------ | ------ | | `estimateBuy` | POST | `/bento/user/bets/estimate-buy` | Wallet | | `estimatedWin` | POST | `/bento/user/bets/estimated-win` | Wallet | | `estimateSell` | POST | `/bento/user/bets/estimate-sell` | Wallet | | `getParentYesPercentageSnapshots` | GET | `/bento/user/bets/yes-percentage-snapshots/parent/:parentMarketId` | Wallet | | `getSellUnlockLiquidity` | GET | `/bento/user/bets/sell-unlock-liquidity/:duelId` | Wallet | | `getUserBetLimit` | GET | `/bento/user/bets/user-bet-limit/:address` | Wallet | | `getUserShares` | GET | `/bento/user/bets/user-shares/:duelId/:address` | Wallet | | `getYesPercentageSnapshots` | GET | `/bento/user/bets/yes-percentage-snapshots/:duelId` | Wallet | | `placeBet` | POST | `/bento/user/bets/create` | Wallet | | `sellBet` | POST | `/bento/user/bets/sell` | Wallet | ### Mutation snippets [#mutation-snippets] `POST /bento/user/bets/estimate-buy` Immediate pricing-engine buy quote (wallet auth required). **`sdk.user.bets.estimateBuy`**, `POST /bento/user/bets/estimate-buy` ```ts await sdk.user.bets.estimateBuy({ /* body */ }); ``` **`sdk.user.bets.estimatedWin`**, `POST /bento/user/bets/estimated-win` ```ts await sdk.user.bets.estimatedWin({ /* body */ }); ``` `POST /bento/user/bets/estimate-sell` **`sdk.user.bets.estimateSell`**, `POST /bento/user/bets/estimate-sell` ```ts await sdk.user.bets.estimateSell({ /* body */ }); ``` Client-side guards that mirror the frontend/chatbot betting path, so the two failure modes that return an opaque backend 500 are caught before the request: - an empty `bet` option label, and - a below-minimum amount (the platform floor is 5 units per option; `estimateBuy` does NOT enforce it, only placement does — which is why a sub-5 bet prices fine then 500s at placement). The amount check only runs when `tokenDecimals` is known (needed to convert base units to whole units) so it can never false-positive. **`sdk.user.bets.placeBet`**, `POST /bento/user/bets/create` ```ts await sdk.user.bets.placeBet({ /* body */ }); ``` `POST /bento/user/bets/sell` — accepted / eventual only. **`sdk.user.bets.sellBet`**, `POST /bento/user/bets/sell` ```ts await sdk.user.bets.sellBet({ /* body */ }); ``` ## sdk.user.duelInvitations [#sdkuserduelinvitations] Namespace `sdk.user.duelInvitations`, 12 method(s). Example: ```ts await sdk.user.duelInvitations.addMember({ /* body */ }); ``` | Method | HTTP | Path | Auth | | ----------------- | ---- | ------------------------------------------------------- | ------ | | `addMember` | POST | `/bento/user/duel-invitations/add-member` | Wallet | | `checkMembership` | GET | `/bento/user/duel-invitations/membership/:param/:param` | Wallet | | `create` | POST | `/bento/user/duel-invitations/create` | Wallet | | `getByInviteCode` | GET | `/bento/user/duel-invitations/:param` | Wallet | | `join` | POST | `/bento/user/duel-invitations/join` | Wallet | | `listForDuel` | GET | `/bento/user/duel-invitations/duel/:param` | Wallet | | `listMemberships` | GET | `/bento/user/duel-invitations/memberships/:param` | Wallet | | `removeMember` | POST | `/bento/user/duel-invitations/remove-member` | Wallet | | `revoke` | POST | `/bento/user/duel-invitations/revoke` | Wallet | | `syncJoinSuccess` | POST | `/bento/user/duel-invitations/sync-join-success` | Wallet | | `userJoin` | POST | `/bento/user/duel-invitations/user-join` | Wallet | | `validateInvite` | POST | `/bento/user/duel-invitations/validate-invite` | Wallet | ### Mutation snippets [#mutation-snippets-1] **`sdk.user.duelInvitations.addMember`**, `POST /bento/user/duel-invitations/add-member` ```ts await sdk.user.duelInvitations.addMember({ /* body */ }); ``` **`sdk.user.duelInvitations.create`**, `POST /bento/user/duel-invitations/create` ```ts await sdk.user.duelInvitations.create({ /* body */ }); ``` **`sdk.user.duelInvitations.join`**, `POST /bento/user/duel-invitations/join` ```ts await sdk.user.duelInvitations.join({ /* body */ }); ``` **`sdk.user.duelInvitations.removeMember`**, `POST /bento/user/duel-invitations/remove-member` ```ts await sdk.user.duelInvitations.removeMember({ /* body */ }); ``` **`sdk.user.duelInvitations.revoke`**, `POST /bento/user/duel-invitations/revoke` ```ts await sdk.user.duelInvitations.revoke({ /* body */ }); ``` **`sdk.user.duelInvitations.syncJoinSuccess`**, `POST /bento/user/duel-invitations/sync-join-success` ```ts await sdk.user.duelInvitations.syncJoinSuccess({ /* body */ }); ``` **`sdk.user.duelInvitations.userJoin`**, `POST /bento/user/duel-invitations/user-join` ```ts await sdk.user.duelInvitations.userJoin({ /* body */ }); ``` **`sdk.user.duelInvitations.validateInvite`**, `POST /bento/user/duel-invitations/validate-invite` ```ts await sdk.user.duelInvitations.validateInvite({ /* body */ }); ``` ## sdk.user.duels [#sdkuserduels] Namespace `sdk.user.duels`, 8 method(s). Example: ```ts await sdk.user.duels.createDuel({ /* body */ }); ``` | Method | HTTP | Path | Auth | | -------------------- | ---- | --------------------------------------------- | ------ | | `createDuel` | POST | `/bento/user/duels/create` | Wallet | | `finalizeContest` | POST | `/bento/user/duels/finalize-contest` | Wallet | | `getContests` | GET | `/bento/user/duels/contest/:param` | Wallet | | `getCreatorContests` | GET | `/bento/user/duels/:param/contests` | Wallet | | `getMyContest` | GET | `/bento/user/duels/contest/:param/my-contest` | Wallet | | `getParticipants` | GET | `/bento/user/duels/participants/:param` | Wallet | | `resolve` | POST | `/bento/user/duels/resolve` | Wallet | | `submitContest` | POST | `/bento/user/duels/contest` | Wallet | ### Mutation snippets [#mutation-snippets-2] `POST /bento/user/duels/create` Submits on-chain market creation. HTTP 201 returns `duelId` + `txHash` but full catalog visibility is still eventual — poll `PublicClient.getDuelById`. **`sdk.user.duels.createDuel`**, `POST /bento/user/duels/create` ```ts await sdk.user.duels.createDuel({ /* body */ }); ``` **`sdk.user.duels.finalizeContest`**, `POST /bento/user/duels/finalize-contest` ```ts await sdk.user.duels.finalizeContest({ /* body */ }); ``` **`sdk.user.duels.resolve`**, `POST /bento/user/duels/resolve` ```ts await sdk.user.duels.resolve({ /* body */ }); ``` **`sdk.user.duels.submitContest`**, `POST /bento/user/duels/contest` ```ts await sdk.user.duels.submitContest({ /* body */ }); ``` ## sdk.user.packs [#sdkuserpacks] Namespace `sdk.user.packs`, 11 method(s). Example: ```ts await sdk.user.packs.addMarket({ /* body */ }); ``` | Method | HTTP | Path | Auth | | ------------------ | ---- | ------------------------------------------- | ------ | | `addMarket` | POST | `/bento/packs/creator/packs/:param/markets` | Wallet | | `createPack` | POST | `/bento/packs/creator/packs` | Wallet | | `enter` | POST | `/bento/packs/:param/enter` | Wallet | | `estimatePick` | POST | `/bento/packs/:param/picks/estimate` | Wallet | | `getMyEntry` | GET | `/bento/packs/:param/me` | Wallet | | `getPayoutProof` | GET | `/bento/packs/:param/payout-proof` | Wallet | | `getPayoutSummary` | GET | `/bento/packs/:param/payout-summary` | Wallet | | `getRefundProof` | GET | `/bento/packs/:param/refund-proof` | Wallet | | `getRefundSummary` | GET | `/bento/packs/:param/refund-summary` | Wallet | | `placePick` | POST | `/bento/packs/:param/picks` | Wallet | | `publish` | POST | `/bento/packs/creator/packs/:param/publish` | Wallet | ### Mutation snippets [#mutation-snippets-3] **`sdk.user.packs.addMarket`**, `POST /bento/packs/creator/packs/:param/markets` ```ts await sdk.user.packs.addMarket({ /* body */ }); ``` **`sdk.user.packs.createPack`**, `POST /bento/packs/creator/packs` ```ts await sdk.user.packs.createPack({ /* body */ }); ``` **`sdk.user.packs.enter`**, `POST /bento/packs/:param/enter` ```ts await sdk.user.packs.enter({ /* body */ }); ``` **`sdk.user.packs.estimatePick`**, `POST /bento/packs/:param/picks/estimate` ```ts await sdk.user.packs.estimatePick({ /* body */ }); ``` **`sdk.user.packs.placePick`**, `POST /bento/packs/:param/picks` ```ts await sdk.user.packs.placePick({ /* body */ }); ``` **`sdk.user.packs.publish`**, `POST /bento/packs/creator/packs/:param/publish` ```ts await sdk.user.packs.publish({ /* body */ }); ``` ## sdk.user.parentMarkets [#sdkuserparentmarkets] Namespace `sdk.user.parentMarkets`, 9 method(s). Example: ```ts await sdk.user.parentMarkets.addChildDuel({ /* body */ }); ``` | Method | HTTP | Path | Auth | | -------------------- | ---- | ----------------------------------------------- | ------ | | `addChildDuel` | POST | `/bento/user/parent-markets/:param/add-duel` | Wallet | | `createInvitation` | POST | `/bento/user/parent-markets/create-invitation` | Wallet | | `createParentMarket` | POST | `/bento/user/parent-markets/create` | Wallet | | `getById` | GET | `/bento/user/parent-markets/:param` | Wallet | | `join` | POST | `/bento/user/parent-markets/join` | Wallet | | `listAccessible` | GET | `/bento/user/parent-markets/accessible` | Wallet | | `listInvitations` | GET | `/bento/user/parent-markets/:param/invitations` | Wallet | | `listMembers` | GET | `/bento/user/parent-markets/:param/members` | Wallet | | `validateInvite` | POST | `/bento/user/parent-markets/validate-invite` | Wallet | ### Mutation snippets [#mutation-snippets-4] **`sdk.user.parentMarkets.addChildDuel`**, `POST /bento/user/parent-markets/:param/add-duel` ```ts await sdk.user.parentMarkets.addChildDuel({ /* body */ }); ``` **`sdk.user.parentMarkets.createInvitation`**, `POST /bento/user/parent-markets/create-invitation` ```ts await sdk.user.parentMarkets.createInvitation({ /* body */ }); ``` **`sdk.user.parentMarkets.createParentMarket`**, `POST /bento/user/parent-markets/create` ```ts await sdk.user.parentMarkets.createParentMarket({ /* body */ }); ``` **`sdk.user.parentMarkets.join`**, `POST /bento/user/parent-markets/join` ```ts await sdk.user.parentMarkets.join({ /* body */ }); ``` **`sdk.user.parentMarkets.validateInvite`**, `POST /bento/user/parent-markets/validate-invite` ```ts await sdk.user.parentMarkets.validateInvite({ /* body */ }); ``` ## sdk.user.polymarket [#sdkuserpolymarket] Namespace `sdk.user.polymarket`, 31 method(s). Example: ```ts await sdk.user.polymarket.approveTrading({ /* body */ }); ``` | Method | HTTP | Path | Auth | | ---------------------- | ------ | ------------------------------------------------- | ------ | | `approveTrading` | POST | `/bento/user/polymarket/approve-trading` | Wallet | | `cancelAll` | DELETE | `/bento/user/polymarket/cancel-all` | Wallet | | `cancelOrder` | DELETE | `/bento/user/polymarket/cancel-order/:param` | Wallet | | `deploySafe` | POST | `/bento/user/polymarket/deploy-safe` | Wallet | | `deployWallet` | POST | `/bento/user/polymarket/deploy-wallet` | Wallet | | `deposit` | POST | `/bento/user/polymarket/deposit` | Wallet | | `deriveCredentials` | POST | `/bento/user/polymarket/derive-credentials` | Wallet | | `getApprovalStatus` | GET | `/bento/user/polymarket/approval-status` | Wallet | | `getAuthStatus` | GET | `/bento/user/polymarket/auth-status` | Wallet | | `getBalance` | GET | `/bento/user/polymarket/balance` | Wallet | | `getCardPrices` | POST | `/bento/user/polymarket/discovery/card-prices` | Wallet | | `getDepositStatus` | GET | `/bento/user/polymarket/deposit-status/:param` | Wallet | | `getEventsByTags` | GET | `/bento/user/polymarket/discovery/events-by-tags` | Wallet | | `getLiveScores` | GET | `/bento/user/polymarket/discovery/live-scores` | Wallet | | `getOrders` | GET | `/bento/user/polymarket/orders` | Wallet | | `getPolygonBalances` | GET | `/bento/user/polymarket/polygon-balances` | Wallet | | `getPositions` | GET | `/bento/user/polymarket/positions` | Wallet | | `getRelayerTx` | GET | `/bento/user/polymarket/relayer-tx/:param` | Wallet | | `getSafeStatus` | GET | `/bento/user/polymarket/safe-status` | Wallet | | `getSportsMap` | GET | `/bento/user/polymarket/discovery/sports-map` | Wallet | | `getTickSize` | GET | `/bento/user/polymarket/tick-size` | Wallet | | `getTrades` | GET | `/bento/user/polymarket/trades` | Wallet | | `getWalletStatus` | GET | `/bento/user/polymarket/wallet-status` | Wallet | | `placeOrder` | POST | `/bento/user/polymarket/place-order` | Wallet | | `publicSearch` | GET | `/bento/user/polymarket/discovery/public-search` | Wallet | | `recoverFunds` | POST | `/bento/user/polymarket/recover-funds` | Wallet | | `redeem` | POST | `/bento/user/polymarket/redeem` | Wallet | | `refreshCredentials` | POST | `/bento/user/polymarket/refresh-credentials` | Wallet | | `withdraw` | POST | `/bento/user/polymarket/withdraw` | Wallet | | `withdrawClob` | POST | `/bento/user/polymarket/withdraw-clob` | Wallet | | `wrapLegacyCollateral` | POST | `/bento/user/polymarket/wrap-legacy-collateral` | Wallet | ### Mutation snippets [#mutation-snippets-5] **`sdk.user.polymarket.approveTrading`**, `POST /bento/user/polymarket/approve-trading` ```ts await sdk.user.polymarket.approveTrading({ /* body */ }); ``` **`sdk.user.polymarket.cancelAll`**, `DELETE /bento/user/polymarket/cancel-all` ```ts await sdk.user.polymarket.cancelAll({ /* body */ }); ``` **`sdk.user.polymarket.cancelOrder`**, `DELETE /bento/user/polymarket/cancel-order/:param` ```ts await sdk.user.polymarket.cancelOrder({ /* body */ }); ``` **`sdk.user.polymarket.deploySafe`**, `POST /bento/user/polymarket/deploy-safe` ```ts await sdk.user.polymarket.deploySafe({ /* body */ }); ``` **`sdk.user.polymarket.deployWallet`**, `POST /bento/user/polymarket/deploy-wallet` ```ts await sdk.user.polymarket.deployWallet({ /* body */ }); ``` **`sdk.user.polymarket.deposit`**, `POST /bento/user/polymarket/deposit` ```ts await sdk.user.polymarket.deposit({ /* body */ }); ``` **`sdk.user.polymarket.deriveCredentials`**, `POST /bento/user/polymarket/derive-credentials` ```ts await sdk.user.polymarket.deriveCredentials({ /* body */ }); ``` **`sdk.user.polymarket.getCardPrices`**, `POST /bento/user/polymarket/discovery/card-prices` ```ts await sdk.user.polymarket.getCardPrices({ /* body */ }); ``` **`sdk.user.polymarket.placeOrder`**, `POST /bento/user/polymarket/place-order` ```ts await sdk.user.polymarket.placeOrder({ /* body */ }); ``` **`sdk.user.polymarket.recoverFunds`**, `POST /bento/user/polymarket/recover-funds` ```ts await sdk.user.polymarket.recoverFunds({ /* body */ }); ``` **`sdk.user.polymarket.redeem`**, `POST /bento/user/polymarket/redeem` ```ts await sdk.user.polymarket.redeem({ /* body */ }); ``` **`sdk.user.polymarket.refreshCredentials`**, `POST /bento/user/polymarket/refresh-credentials` ```ts await sdk.user.polymarket.refreshCredentials({ /* body */ }); ``` *+ 3 more mutations in table above.* ## sdk.user.portfolio [#sdkuserportfolio] Namespace `sdk.user.portfolio`, 6 method(s). Example: ```ts await sdk.user.portfolio.getAccountDetails({ /* body */ }); ``` | Method | HTTP | Path | Auth | | ------------------- | ---- | ---------------------------------------- | ------ | | `getAccountDetails` | POST | `/bento/user/portfolio/accountDetails` | Wallet | | `getDuels` | POST | `/bento/user/portfolio/duels` | Wallet | | `getDuelsTable` | POST | `/bento/user/portfolio/table/duels` | Wallet | | `getHistoryTable` | POST | `/bento/user/portfolio/table/history` | Wallet | | `getPnlChart` | POST | `/bento/user/portfolio/pnl-chart` | Wallet | | `getPositions` | GET | `/bento/user/portfolio/positions/:param` | Wallet | ### Mutation snippets [#mutation-snippets-6] **`sdk.user.portfolio.getAccountDetails`**, `POST /bento/user/portfolio/accountDetails` ```ts await sdk.user.portfolio.getAccountDetails({ /* body */ }); ``` **`sdk.user.portfolio.getDuels`**, `POST /bento/user/portfolio/duels` ```ts await sdk.user.portfolio.getDuels({ /* body */ }); ``` **`sdk.user.portfolio.getDuelsTable`**, `POST /bento/user/portfolio/table/duels` ```ts await sdk.user.portfolio.getDuelsTable({ /* body */ }); ``` **`sdk.user.portfolio.getHistoryTable`**, `POST /bento/user/portfolio/table/history` ```ts await sdk.user.portfolio.getHistoryTable({ /* body */ }); ``` **`sdk.user.portfolio.getPnlChart`**, `POST /bento/user/portfolio/pnl-chart` ```ts await sdk.user.portfolio.getPnlChart({ /* body */ }); ``` ## sdk.user.referralAnalytics [#sdkuserreferralanalytics] Namespace `sdk.user.referralAnalytics`, 1 method(s). Example: ```ts await sdk.user.referralAnalytics.getMine(/* args */); ``` | Method | HTTP | Path | Auth | | --------- | ---- | -------------------------------- | ------ | | `getMine` | GET | `/bento/user/referral-analytics` | Wallet | ## sdk.user.withdraw [#sdkuserwithdraw] Namespace `sdk.user.withdraw`, 4 method(s). Example: ```ts await sdk.user.withdraw.claimFees({ /* body */ }); ``` | Method | HTTP | Path | Auth | | ---------------- | ---- | ------------------------------------- | ------ | | `claimFees` | POST | `/bento/user/withdraw/claim-fees` | Wallet | | `claimWinnings` | POST | `/bento/user/withdraw/claim-winnings` | Wallet | | `getCreatorFees` | GET | `/bento/user/withdraw/creator-fees` | Wallet | | `withdraw` | POST | `/bento/user/withdraw` | Wallet | ### Mutation snippets [#mutation-snippets-7] **`sdk.user.withdraw.claimFees`**, `POST /bento/user/withdraw/claim-fees` ```ts await sdk.user.withdraw.claimFees({ /* body */ }); ``` **`sdk.user.withdraw.claimWinnings`**, `POST /bento/user/withdraw/claim-winnings` ```ts await sdk.user.withdraw.claimWinnings({ /* body */ }); ``` **`sdk.user.withdraw.withdraw`**, `POST /bento/user/withdraw` ```ts await sdk.user.withdraw.withdraw({ /* body */ }); ```