> ## Documentation Index
> Fetch the complete documentation index at: https://docs.0xmonaco.com/llms.txt
> Use this file to discover all available pages before exploring further.

# BuilderCodes

> Inspect and claim a frontend's accrued per-trade revenue share

```typescript theme={null}
import { MonacoSDK } from "@0xmonaco/core";

const config = await sdk.buildercodes.getConfig();
await sdk.buildercodes.setBuilderFee({ bps: 10 });
const feeChanges = await sdk.buildercodes.listBuilderFeeChanges({ pageSize: 20 });
const history = await sdk.buildercodes.listPayouts({ pageSize: 20 });
const summary = await sdk.buildercodes.getPayoutSummary();
const rewards = await sdk.buildercodes.getRewardsBalance();

// Claim accrued BuilderCodes payout balance into a trading balance
await sdk.buildercodes.claimBuildercodeRewards({ token, amount });
```

BuilderCodes provides per-trade frontend payouts. An enrolled frontend can earn
Monaco revenue share and can charge its own builder fee. Every positive builder
fee charged to a taker is credited in full to the enrolled application's
claimable payout balance with kind `BUILDER_FEE`; revenue-share credits use
`REVENUE_SHARE`. Claiming moves the reserved balance into a spendable trading
balance.

A claim is a ledger-only move: it debits the payout bucket and credits the
payout wallet's trading balance under the authenticated application. There is no
on-chain transaction and no gas, and the credited funds are immediately
tradeable. To take the money off the exchange, claim it and then use the
ordinary [withdrawal flow](/sdk/typescript/vault).

For taker-side attribution, how the revenue share is reported, and
payout-address rotation behavior, see the
[BuilderCodes developer guide](/developers/buildercodes).

***

All BuilderCodes methods require an authenticated, non-delegated session. No
program switch gates reads or claims, so a balance already credited stays
claimable; `setBuilderFee` needs the revenue share or builder fees switched
on. Application-level reads derive
the application from the session and require the authenticated wallet to match
its configured payout address; no method accepts a caller-selected application
or payout wallet.

<ResponseField name="getConfig" type="() => Promise<GetBuildercodesConfigResponse>">
  Read the authenticated frontend's current BuilderCodes state.

  **Returns:**

  * `applicationId`: string — authenticated application UUID
  * `enrolled`: boolean — whether the application is currently enrolled
  * `payoutAddress?`: string | null — the configured payout wallet. A string is the address: use it. `null` and `undefined` are the two spellings of "none configured" and are the pair to normalize together — never fold a returned string in with them. In practice a successful response always carries an address, because this endpoint authorizes the caller *as* that wallet and refuses with `403` when none is set; the type admits the absent forms so a nullable wire shape cannot throw
  * `buildercodesEnabled`: boolean — whether the revenue share is on. This read is not gated by it, so a successful response can report `false`
  * `buildercodesBudgetBps`: number — the configured **target** share of Monaco's protocol fee on each taker fill, in basis points from `0` to `10000`. It is what the application is configured to earn, not what any given trade paid out — a payout row's `appliedBps` is the realized rate for that fill and may be lower
  * `builderFeeEnabled`: boolean — whether stored builder fees are currently active
  * `builderFeeBps`: number — the stored builder fee in basis points, including while builder fees are disabled
  * `builderFeeCapBps`: number — the current maximum accepted fee, never above `100`
  * `changesRemaining`: number — `BUILDER` changes still available in the rolling 24-hour window, from `0` to `2`
  * `nextChangeAllowedAt?`: string | null — RFC 3339 time for the next allowed change when none remain

  **Throws:** `401` (not authenticated), `403` (a delegated session, or a wallet that is not the application's payout address), `404` (application not found), `429` (enforced read budget exhausted), or `500` (internal error).
</ResponseField>

<ResponseField name="setBuilderFee" type="(params: SetBuilderFeeParams) => Promise<SetBuilderFeeResponse>">
  Set the authenticated frontend's builder fee. `bps` is required, an integer from `0` through `100`; a real change must also be at or below the current `builderFeeCapBps`. Explicit `0` clears the fee.

  The application must be enrolled with a payout address. At most two frontend changes are allowed in a rolling 24 hours; Monaco administrator changes do not count. Setting the current value succeeds without a log row, email or consumed change, even when `changesRemaining` is `0` or the live cap is below that stored value. A value may be stored while `builderFeeEnabled` is `false` as long as the revenue share is on; the call is refused only while both are off. Charging is capped at the live cap and requires enrollment. A newly stored fee is charged once the matching engine's next BuilderCodes enrollment refresh sequences it, within about 30 seconds by default. Sequencer ordering is authoritative for the builder-fee rate, gate and enrollment: an order processed before one of those sequenced changes uses the earlier state, and a later order uses the new state. Builder fees are charged on taker fills only, so a resting remainder that later fills as maker pays none. Each positive charged fee is credited to the application's payout balance as a `BUILDER_FEE` payout.

  **Returns:** `builderFeeBps`, `previousBuilderFeeBps`, `changesRemaining`, and nullable/absent `nextChangeAllowedAt`.

  **Throws:** `400` for missing/invalid `bps` or a real change above the cap; `401` without valid authentication; `403` when neither the revenue share nor builder fees are switched on, the session is delegated, or the caller is not the application's payout wallet (including when none is configured); `404` when the application is absent; `409` when enrollment is incomplete after ownership passes or the change limit is exhausted; `500` on an internal failure. A limit response carries `details.reason: "builder_fee_change_limit"` and `details.nextChangeAllowedAt`. Server keys are not accepted. A maintenance block returns `503` before the write.
</ResponseField>

<ResponseField name="listBuilderFeeChanges" type="(params?: ListBuilderFeeChangesParams) => Promise<ListBuilderFeeChangesResponse>">
  List builder-fee changes newest first. `pageSize?` defaults to `20` and is at most `100`; pass an opaque `pageToken?` from the prior `nextPageToken` to continue.

  **Returns:** `changes`, each with `actorType` (`"BUILDER"` or `"ADMIN"`), `actor`, `oldBps`, `newBps`, and RFC 3339 `createdAt`, plus `nextPageToken` (empty at the end). For `BUILDER`, `actor` is the payout wallet. For `ADMIN`, it is the Monaco operator's email address.

  **Throws:** `400` for invalid pagination; `401` without valid authentication; `403` for a delegated session or a caller that is not the application's payout wallet; `404` when the application is absent; `429` when the enforced read budget is exhausted; `500` on an internal failure. Server keys are not accepted.
</ResponseField>

<ResponseField name="listPayouts" type="(params?: ListBuildercodePayoutsParams) => Promise<ListBuildercodePayoutsResponse>">
  List the authenticated application's per-trade credits, newest first across persistor batches — see the ordering note below for what that does and does not guarantee inside one batch.

  **Parameters:** `pageSize?` defaults to `20` and is at most `1000`, and the server rejects a value out of range with `400` rather than clamping it. `pageToken?` is an opaque cursor: omit it (or send `""`) to start from the newest credit, then pass the prior response's `nextPageToken` to continue. Page-number pagination was removed: there is no `page`, the SDK rejects a leftover one before a request is sent, and the server answers `400`. `kind?` is `"REVENUE_SHARE"` or `"BUILDER_FEE"` and filters the list; omit it for every credit. The SDK rejects any other value before a request is sent, and the server returns `400` for one.

  **Ordering change.** The cursor walks `(createdAt, id)` descending. That key is total and stable — a page can neither skip nor duplicate a credit — but it is **not chronological inside one persistor batch**: `createdAt` defaults to the transaction-start `NOW()`, so every credit one batch writes shares a timestamp, and the `id` tie-break is a random UUID. Within a batch the order is therefore arbitrary, and a later fill can appear ahead of an earlier one. The removed page-number order led with the durable-log `sequence` and its ordinal precisely because those carry the engine's fill order, and one entry routinely holds several credits (a taker sweeping several makers, or a flattened batch create). Across batches the order is still newest-first.

  **Throws:** `400` (invalid pagination or `kind`), `401` (not authenticated), `403` (a delegated session, or a wallet that is not the application's payout address), `404` (application not found), `429` (read budget exhausted), or `500` (internal error).

  **Returns:** `payouts`, the opaque `nextPageToken` (empty once the walk is exhausted — it is one ordered read, so a short page is definitive), plus numeric `total`, `pageSize` and `totalPages` fields; with `kind`, the counts cover that kind only. Each payout contains `tradeId`, the `payoutAddress` at credit time, `token`, RAW atomic-unit `amount`, `appliedBps`, RFC 3339 `createdAt`, and `kind`. For `BUILDER_FEE`, `appliedBps` is the normalized builder-fee rate charged to the taker. For `REVENUE_SHARE`, it is the realized truncated whole-bps rate for that fill and may be lower than `buildercodesBudgetBps`.
</ResponseField>

<ResponseField name="getPayoutSummary" type="() => Promise<GetBuildercodePayoutSummaryResponse>">
  Summarize earnings without combining unlike tokens.

  **Returns:** numeric `totalPayouts`, nullable/absent `lastCreditedAt`, `byToken`, and `byTokenAndKind`. Each `byToken` total contains `token`, RAW atomic-unit `totalAmount`, numeric `payoutCount`, and RFC 3339 `lastCreditedAt`, summed across both payout kinds. `byTokenAndKind` splits the same totals by `kind` (`REVENUE_SHARE` or `BUILDER_FEE`), one row per token and kind with at least one credit; a token's rows add up to its `byToken` total. With no payouts, `totalPayouts` is `0`, both lists are empty, and `lastCreditedAt` is null/absent.
</ResponseField>

<ResponseField name="getRewardsBalance" type="() => Promise<GetBuildercodeRewardsBalanceResponse>">
  Read the authenticated wallet's reserved BuilderCodes balances. The bucket is derived server-side from that wallet; current enrollment is not required, so a former payout wallet can still inspect and claim what it accrued.

  **Returns:** `balances`, one `{ token, available }` entry per token with a non-zero available RAW atomic-unit balance, or an empty array.
</ResponseField>

***

<ResponseField name="claimBuildercodeRewards" type="(params: ClaimBuildercodeRewardsParams) => Promise<ClaimBuildercodeRewardsResponse>">
  Move accrued BuilderCodes payout balance out of your reserved payout bucket and into a
  trading balance.

  Requires authentication. The payout bucket is resolved server-side from the authenticated
  caller's own wallet address — it is never taken from the request body — so a caller can
  only ever claim their own accrued balance.

  **Parameters:** `ClaimBuildercodeRewardsParams`:

  * `token`: string — payout token contract address (0x-prefixed). BuilderCodes shares accrue in the quote token of the traded pair.
  * `amount`: string — amount to claim, in RAW atomic units of the token. Must be a positive integer and no greater than the accrued balance; an over-claim is rejected rather than partially filled.

  **Returns:** `ClaimBuildercodeRewardsResponse`:

  * `payoutBalance`: string — your payout-bucket balance after the claim, RAW atomic units
  * `tradingBalance`: string — your trading balance for the token after the claim, RAW atomic units

  **Throws:**

  * `403` when the session is a delegated agent — a permanently unsupported session type for claims, not a rollout state.
  * `409` when the payout balance is insufficient for the requested amount — including when the wallet has never accrued a share
  * `429` when the per-user [movement budget](/developers/rate-limits) the claim shares with withdrawals and collateral transfers is enforced and spent. It is refused before the claim reaches the sequencer, so nothing committed and it is safe to retry after `retryAfter`
  * `503` when the matching engine is unreachable, restarting, or slow to answer — and, more broadly, **any** untagged `5xx` or a transport failure that returned no response at all. The outcome is **unknown**, not a rejection — the claim is admitted before its reply is sent, so it may still commit, and with no idempotency key a blind retry can claim twice. Do **not** retry automatically, and do not settle it with a balance read — an admitted claim may still be queued, so an unchanged balance does not prove it will not commit. Treat the claim as pending and reconcile before issuing another. The SDK backs that up: it clears `retryable` on every failure whose outcome it cannot decide — any status at or above `500`, and a failure with no status at all because the response was never seen — against its usual "every 5xx is retryable" rule, so retry logic driven off that flag cannot claim twice.
  * Two exceptions stay retryable, because both are refused before the claim can be admitted and so committed nothing: a maintenance block (`error: "OPERATIONS_BLOCKED"`) and a sequencer overload (`code: "OVERLOADED"`). An overload is the one 503 that positively wants a retry with backoff. Every `4xx` also keeps its own classification, being a decided outcome.
</ResponseField>

## Who can claim

The claim is authorized for the payout wallet itself, and it moves only that
wallet's balance. Three consequences are worth knowing before you integrate:

* **The payout wallet may be an EOA or a smart-contract wallet.** An EOA answers
  Monaco's signature challenge directly. A multisig such as a Safe holds no
  private key and authenticates by **EIP-1271** contract signature instead, with
  Monaco asking the wallet contract whether it accepts the challenge. That path
  is offered only to a wallet holding a BuilderCodes payout bucket, only while
  `contract_wallet_auth_enabled` is on, and only where the deployment has a
  chain RPC endpoint configured; while any of those is unmet, login is EOA-only
  and a contract payout wallet can call neither this endpoint nor the withdrawal
  flow that follows. While that path is enabled, challenges stay valid for 30
  minutes rather than 5 — for every address, not only eligible ones, so the
  unauthenticated challenge response cannot reveal which addresses hold a payout
  bucket — since collecting owner approvals takes longer than a single click.

* **A wallet that never earned a share has nothing to claim.** There is no
  separate "not enrolled" error; an empty bucket and a bucket that never existed
  both report an insufficient balance, because in both cases there is no balance
  to move.

* **Payout-address changes are not retroactive, and claiming follows that.**
  When a frontend changes its configured payout address, future shares accrue to
  the new wallet while everything already accrued stays with the old one.
  Claiming deliberately does not require *current* enrollment, so the previous
  wallet can still claim what it earned while it was the payout address.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.