---
title: Setup Guide
description: "Wire up AGG for your app — client setup, provider stack, auth, and WebSocket"
---

> **For AI agents:** the complete documentation index is at [llms.txt](/llms.txt). Append `.md` to any page URL for its markdown version.

# Setup Guide

Choose your integration path and follow the setup for your layer. See the
[Packages overview](/packages/overview) for installation, package descriptions, and peer dependencies.

<Info>
  Need sign-in flows? Start with [Authentication](/recipes/authentication). For wire-level streaming details,
  see [WebSocket Protocol](/api/websocket). For end-to-end implementations, see
  [Real-Time Orderbook](/recipes/websocket-orderbook) and
  [Real-Time Charts](/recipes/websocket-charts). For theming and partner branding, see
  [Customize UI](/components/customization).
</Info>

<Tabs>
  <Tab title="SDK (vanilla JS/TS)">
    <div key="sdk-vanilla-js-ts" className="[&>p]:mb-4 [&>p:last-child]:mb-0">
      No framework dependency. Works in browsers, Node.js, and React Native.

      ```typescript
      import { createAggClient, CandleBuilder } from "@agg-build/sdk";

      const client = createAggClient({
        baseUrl: "https://api.agg.market",
        appId: "your-app-id",
        wsUrl: "wss://ws.agg.market/ws",
      });

      // REST: fetch events, orderbooks, charts
      const events = await client.getVenueEvents({ limit: 10 });
      const books = await client.getOrderbooks({
        venueMarketIds: ["your-market-id"],
        depth: 20,
      });
      const bars = await client.getChartBars({
        venueMarketOutcomeId: "...",
        resolution: "5m",
        to: Date.now(),
        countBack: 200,
      });
      const route = await client.getSmartRoute({ venueMarketId: "...", maxSpend: 50, side: "yes" });

      const book = books.data[0];
      if (book?.status !== "ok") {
        throw new Error(book?.error?.message ?? "No live orderbook available");
      }

      // WebSocket: real-time orderbook + trades
      const builder = new CandleBuilder();
      const ws = client.createWebSocket({
        onSnapshot: (id, book) => {
          if (book.midpoint != null) builder.addMidpoint(book.midpoint, book.timestamp);
        },
        onDelta: (id, book) => {
          if (book.midpoint != null) builder.addMidpoint(book.midpoint, book.timestamp);
        },
        onTrade: (trade) => {
          builder.addTrade(trade.price, trade.size, trade.timestamp);
        },
      });

      ws.subscribe("your-market-id");

      // Read candles for any chart library
      builder.onChange(() => {
        const candles = builder.getClosed("5m");
        const forming = builder.getForming("5m");
        yourChart.update(candles, forming);
      });
      ```
    </div>
  </Tab>

  <Tab title="React (hooks)">
    <div key="react-hooks" className="[&>p]:mb-4 [&>p:last-child]:mb-0">
      Bring your own UI. Hooks handle WS subscriptions, caching, and cleanup.

      ```tsx
      import { AggProvider, QueryClient, QueryClientProvider } from "@agg-build/hooks";
      import { createAggClient } from "@agg-build/sdk";

      const client = createAggClient({
        baseUrl: "https://api.agg.market",
        appId: "your-app-id",
        wsUrl: "wss://ws.agg.market/ws",
      });

      const queryClient = new QueryClient();

      function App() {
        return (
          <QueryClientProvider client={queryClient}>
            <AggProvider client={client}>
              <MyApp />
            </AggProvider>
          </QueryClientProvider>
        );
      }
      ```

      Then use hooks in any component:

      ```tsx
      import { useLiveMarket, useMarketChart, useSmartRoute } from "@agg-build/hooks";

      function MarketView({ venueMarketId, venueMarketOutcomeId }) {
        const { orderbook } = useLiveMarket(venueMarketId);
        const { data: chart } = useMarketChart({
          marketId: venueMarketOutcomeId,
          interval: "5m",
          startTs: Date.now() - 86_400_000,
          endTs: Date.now(),
        });
        const { data: route } = useSmartRoute({
          venueMarketId,
          maxSpend: 50,
          side: "yes",
        });

        const primaryVenue = chart?.primaryVenue;
        const candles = primaryVenue ? chart.venues[primaryVenue]?.candles ?? [] : [];

        return <YourChart data={candles} orderbook={orderbook} route={route} />;
      }
      ```

      | Hook | What it does |
      |------|-------------|
      | `useLiveMarket(id)` | Live orderbook via WS |
      | `useMarketChart({ marketId, interval, startTs, endTs, countBack })` | Outcome-id chart history, exposed under `data.primaryVenue` |
      | `useSmartRoute({ venueMarketId, maxSpend, side })` | User-scoped route quote across available liquidity |
      | `useLiveTrades(id)` | Real-time trade feed |
      | `useMarketOrderbook({ marketId })` | Aggregated orderbook + venue breakdown |
      | `useMarketArb(marketId)` | Live cross-venue arbitrage return for one market |
      | `useArbFeed()` | Live arbitrage returns for many markets (`byMarket` map + event-level `byEvent` max) |
    </div>
  </Tab>

  <Tab title="React (UI components)">
    <div key="react-ui-components" className="[&>p]:mb-4 [&>p:last-child]:mb-0">
      Pre-built, themed components. Drop in and go.

      ```tsx
      import "@agg-build/ui/styles.css";
      import { AggProvider, QueryClient, QueryClientProvider } from "@agg-build/hooks";
      import { createAggClient } from "@agg-build/sdk";

      const client = createAggClient({
        baseUrl: "https://api.agg.market",
        appId: "your-app-id",
        wsUrl: "wss://ws.agg.market/ws",
      });

      const queryClient = new QueryClient();

      function App() {
        return (
          <QueryClientProvider client={queryClient}>
            <AggProvider
              client={client}
              config={{
                general: {
                  locale: "en-US",
                  theme: "light",
                },
                features: {
                  enableAnimations: true,
                  enableLiveUpdates: true,
                },
              }}
            >
              <MyApp />
            </AggProvider>
          </QueryClientProvider>
        );
      }
      ```

      Then use components:

      ```tsx
      import { EventMarketPage } from "@agg-build/ui/pages";
      import { MarketDetails } from "@agg-build/ui/events";
      import { LineChart } from "@agg-build/ui/primitives";

      // Full event page — hero chart, market cards, orderbooks, all live
      <EventMarketPage eventId="..." />

      // Or individual components
      <MarketDetails event={event} marketId={marketId} defaultTab="graph" />
      <LineChart series={series} chartType="candlestick" height={320} live />
      ```

      Browse the live [Event Market Page reference](/components/pages/event-market-page) and
      [Market Details reference](/components/events/market-details). Use
      [Customize UI](/components/customization) for fonts, colors, copy, formatting, and slot
      overrides.
    </div>
  </Tab>

  <Tab title="React (with auth)">
    <div key="react-with-auth" className="[&>p]:mb-4 [&>p:last-child]:mb-0">
      Full stack with connect/sign-in UI. See [Authentication](/recipes/authentication) for detailed flow examples.

      ```tsx
      import "@agg-build/ui/styles.css";
      import { AggProvider, QueryClient, QueryClientProvider } from "@agg-build/hooks";
      import { createAggClient } from "@agg-build/sdk";
      import { AggAuthProvider, ConnectButton, createGoogleAuthMethod } from "@agg-build/auth";
      import { useSiweAuthMethod } from "@agg-build/auth/siwe";
      import { WagmiProvider } from "wagmi";
      import { wagmiConfig } from "./wagmi-config";

      const client = createAggClient({
        baseUrl: "https://api.agg.market",
        appId: "your-app-id",
        wsUrl: "wss://ws.agg.market/ws",
      });

      const queryClient = new QueryClient();

      function AuthButton() {
        const siwe = useSiweAuthMethod({ statement: "Sign in" });
        return (
          <AggAuthProvider methods={[siwe, createGoogleAuthMethod()]}>
            <ConnectButton />
          </AggAuthProvider>
        );
      }

      function App() {
        return (
          <WagmiProvider config={wagmiConfig}>
            <QueryClientProvider client={queryClient}>
              <AggProvider client={client}>
                <AuthButton />
              </AggProvider>
            </QueryClientProvider>
          </WagmiProvider>
        );
      }
      ```

      Browse the live [Connect Button reference](/components/auth/connect-button).
    </div>
  </Tab>
</Tabs>

## WebSocket endpoint

If you create raw sockets yourself, connect to:

```
wss://ws.agg.market/ws?appId=YOUR_APP_ID
```

The SDK and hooks manage connection, reconnection, resnapshot requests, and orderbook integrity checks automatically.

## Testing mode limits

All new partner apps start in **testing mode** — a sandboxed state that lets you build and
verify your integration without committing to the full live-mode agreement. Two hard caps apply
while an app is in testing mode:

| Limit | Cap | Applies to |
|-------|-----|-----------|
| Users | 20  | Unique users who sign in to your app |
| Trades | 100 | Orders placed (excluding failed orders) |

<Note>
  Existing users can always sign in — the user cap only blocks new account creation once the
  limit is reached. No data is lost when the cap is hit.
</Note>

### How the limits work

When a new user tries to sign in and the app has already registered 20 users, the sign-in
endpoint (`POST /auth/verify`) returns a `403` with a plain-language message explaining the
situation. Likewise, when a new trade is submitted after 100 trades have been placed, the
execution endpoint returns a `403`. Both error responses use the `message` field of the standard
`{ message: string }` error body — you can surface this text directly in your UI without any
special-casing.

```json
// POST /auth/verify — over the user cap
{
  "statusCode": 403,
  "message": "This app has reached its testing limit of 20 users. Sign the partner agreement to enable live mode and continue adding users."
}

// POST /execution/fill — over the trade cap
{
  "statusCode": 403,
  "message": "This app has reached its testing limit of 100 trades. Sign the partner agreement to enable live mode and continue placing trades."
}
```

For OAuth and magic-link sign-in flows, the same error is surfaced as a `message` query
parameter on the redirect URL (alongside an `error=testing_user_limit_reached` param your
frontend can key off).

### Lifting the limits — signing the partner agreement

Once you are ready to go live, sign the partner agreement from the admin dashboard
(**Apps → your app → Go Live**). Signing sets `partnerAgreementSigned = true` on your app and
immediately removes both caps — no restart or config change needed. The agreement is a one-time
action; it cannot be reversed from the dashboard.

After signing:
- New users can sign in without restriction.
- New trades can be placed without restriction.
- Existing testing-mode users and trades are preserved; the historical counts are not reset.

## Next steps

<Columns cols={2}>
  <Card title="Authentication" icon="key" href="/recipes/authentication">
    Add wallet, OAuth, or email sign-in on top of the base client setup.
  </Card>
  <Card title="WebSocket Protocol" icon="brackets-curly" href="/api/websocket">
    Review the wire format, auth upgrade flow, heartbeat, and reconnection behavior.
  </Card>
  <Card title="Real-Time Orderbook" icon="code" href="/recipes/websocket-orderbook">
    SDK, hooks, and UI recipes for live orderbook rendering.
  </Card>
  <Card title="Real-Time Charts" icon="cube" href="/recipes/websocket-charts">
    Build live OHLCV charts from REST history and WebSocket updates.
  </Card>
  <Card title="Customize UI" icon="paintbrush" href="/components/customization">
    Brand AGG components with CSS variables, labels, formatting, and slots.
  </Card>
</Columns>
