diff --git a/builders/authentication.mdx b/builders/authentication.mdx
new file mode 100644
index 0000000..7f796e1
--- /dev/null
+++ b/builders/authentication.mdx
@@ -0,0 +1,69 @@
+---
+title: "Authentication"
+description: "How Builders API keys work."
+---
+
+## Header
+
+Send your key on every request:
+
+```http
+x-api-key: jxn_live_…
+```
+
+The legacy `X-Junction-Api-Key` header is also accepted and carries the
+same key. New integrations should prefer `x-api-key`.
+
+
+Never embed the API key in a front-end bundle or in a public repository.
+The key is server-side only. If you need a web UI that talks to the
+Builders API, proxy through your own backend.
+
+
+## Request a key
+
+Builder API keys are issued to partners who integrate Junction via a
+builder code or a referral code. Two options:
+
+1. [Contact form](https://junction.exchange/contact?reason=builders) —
+ pick the "Builders" reason.
+2. Email [team@junction.exchange](mailto:team@junction.exchange) with your
+ referral code, product name, and expected traffic.
+
+Keys are opaque strings; rotate them by requesting a new one — old keys
+are revoked on rotation.
+
+## Rate limits
+
+All responses expose:
+
+| Header | Meaning |
+| --- | --- |
+| `X-RateLimit-Limit` | Requests allowed in the current window. |
+| `X-RateLimit-Remaining` | Requests left in the current window. |
+| `Retry-After` | Seconds to wait when the server returns `429`. |
+
+Authenticated requests bypass per-IP rate limits. Fair use is expected;
+for bursty dashboards prefer caching client-side for 30 – 120 seconds.
+
+## Which endpoints need the key?
+
+All of them:
+
+- `GET /referrals/{code}`
+- `GET /referrals/volume_30d/{code}`
+- `GET /referrals/{code}/user/{address}`
+- `GET /referrals/{code}/summary`
+
+Without a valid `x-api-key` you get a `401` with
+`{ "success": false, "error": "Invalid or missing API key", "code": "invalid_api_key" }`.
+
+## Test your key
+
+```bash
+curl -i https://services.junction.exchange/builders/referrals/ABC123 \
+ -H "x-api-key: $JXN_API_KEY"
+```
+
+A successful response is `HTTP/2 200` with `content-type: application/json`
+and a `{ "success": true, ... }` body.
diff --git a/builders/commissions.mdx b/builders/commissions.mdx
new file mode 100644
index 0000000..7eb85f0
--- /dev/null
+++ b/builders/commissions.mdx
@@ -0,0 +1,62 @@
+---
+title: "Commissions"
+description: "How tier 1 / 2 / 3 commissions are computed."
+---
+
+## Tier model
+
+When a user signs up with your referral code, they become a **tier 1**
+referral. When one of *their* referrals joins, that person is a **tier 2**
+referral of yours, and so on down to **tier 3**.
+
+| Tier | Who they are | Default commission |
+| --- | --- | --- |
+| 1 | Users who signed up with **your** referral code. | **60%** of their builder fees |
+| 2 | Users who signed up with a tier-1 user's code. | **12%** |
+| 3 | Users who signed up with a tier-2 user's code. | **4%** |
+
+Rates are exposed in every response under `summary.commissionRates` so
+clients do not hard-code them. Junction operates individual partners with
+custom rates — the response always reflects the rates applied to your
+account.
+
+## Computation
+
+```
+commissionEarned = builderFees * (commissionRate / 100)
+```
+
+Where `builderFees` is the cumulative USD value of Hyperliquid builder fees
+paid by the referred user. Builder fees themselves default to:
+
+- **0.05%** of notional on Hyperliquid perps
+- **0.10%** of notional on Hyperliquid spot
+
+These are the Junction defaults; your contract may specify different
+rates.
+
+## Windows
+
+- `GET /referrals/{code}` returns **cumulative** (all-time) volume and
+ commission totals.
+- `GET /referrals/volume_30d/{code}` is shape-compatible with based.one's
+ 30-day endpoint. Today it returns the cumulative snapshot with a
+ `period.days = 30` stamp; this is disclosed in `meta.notes`. The
+ **per-user** endpoint (`/user/{address}`) uses the Hyperliquid fills API
+ so its `days` window is always exact.
+
+## Data sources and freshness
+
+- Referral tree walk and cumulative totals come from Junction's referral
+ service.
+- Per-user asset breakdown and trade list come from Hyperliquid's
+ `userFillsByTime` info endpoint.
+- Tree snapshots are cached for 120 seconds on the Builders API Worker to
+ keep latency stable under burst.
+
+## Configurable rates
+
+Rates are exposed as environment variables on the Builders API Worker
+(`BUILDERS_TIER1_BPS`, `BUILDERS_TIER2_BPS`, `BUILDERS_TIER3_BPS`). Values
+are in basis points of builder fees (6000 = 60%). Contact the Junction
+team if you need a custom grid.
diff --git a/builders/endpoints/list-referrals.mdx b/builders/endpoints/list-referrals.mdx
new file mode 100644
index 0000000..803f60e
--- /dev/null
+++ b/builders/endpoints/list-referrals.mdx
@@ -0,0 +1,109 @@
+---
+title: "List referrals"
+description: "All referrals (tier 1 / 2 / 3) under a code, cumulative."
+openapi: "GET /builders/referrals/{code}"
+---
+
+Returns every user who is a direct or indirect referral of `code`, with
+their cumulative trading volume, builder fees paid, and the commission you
+have earned. Sortable and filterable by tier.
+
+## Example request
+
+
+```bash curl
+curl "https://services.junction.exchange/builders/referrals/ABC123?sortBy=totalTraded&sortDirection=desc&tier=tier1" \
+ -H "x-api-key: $JXN_API_KEY"
+```
+
+```typescript TypeScript
+const qs = new URLSearchParams({
+ sortBy: 'totalTraded',
+ sortDirection: 'desc',
+ tier: 'tier1',
+})
+const res = await fetch(
+ `https://services.junction.exchange/builders/referrals/ABC123?${qs}`,
+ { headers: { 'x-api-key': process.env.JXN_API_KEY! } },
+)
+const body = await res.json()
+```
+
+```python Python
+import os, requests
+
+r = requests.get(
+ "https://services.junction.exchange/builders/referrals/ABC123",
+ params={"sortBy": "totalTraded", "sortDirection": "desc", "tier": "tier1"},
+ headers={"x-api-key": os.environ["JXN_API_KEY"]},
+ timeout=10,
+)
+r.raise_for_status()
+body = r.json()
+```
+
+
+## Example response
+
+```json
+{
+ "success": true,
+ "data": {
+ "referralCode": "ABC123",
+ "referralOwner": {
+ "address": "0x1234567890abcdef1234567890abcdef12345678",
+ "code": "ABC123"
+ },
+ "window": "cumulative",
+ "referrals": [
+ {
+ "address": "0xabcdef1234567890abcdef1234567890abcdef12",
+ "registrationDate": "2026-01-15T10:30:00.000Z",
+ "tier": 1,
+ "totalTraded": 15750.25,
+ "builderFees": 7.87,
+ "commissionEarned": 4.72,
+ "commissionRate": 60,
+ "referreeCode": "ABC123",
+ "swapXp": 120,
+ "referralXp": 35,
+ "totalXp": 155
+ }
+ ],
+ "summary": {
+ "tier1Count": 15,
+ "tier2Count": 8,
+ "tier3Count": 3,
+ "totalTier1Commission": 2450.75,
+ "totalTier2Commission": 245.30,
+ "totalTier3Commission": 48.15,
+ "totalVolume": 41200.12,
+ "totalCommission": 2744.20,
+ "commissionRates": { "tier1": 60, "tier2": 12, "tier3": 4 }
+ },
+ "meta": {
+ "source": "referral-service",
+ "notes": [
+ "Volume and commission figures are cumulative (all-time).",
+ "Commission rates are configurable and default to 60%/12%/4% of builder fees for tier 1/2/3."
+ ],
+ "generatedAt": "2026-04-18T12:34:56.000Z"
+ }
+ }
+}
+```
+
+## Query parameters
+
+| Name | Type | Default | Values |
+| --- | --- | --- | --- |
+| `sortBy` | string | `registrationDate` | `registrationDate`, `totalTraded`, `commissionEarned` |
+| `sortDirection` | string | `desc` | `asc`, `desc` |
+| `tier` | string | `all` | `all`, `tier1`, `tier2`, `tier3` |
+
+## See also
+
+- [30-day window](/builders/endpoints/volume-30d)
+- [User detail](/builders/endpoints/user-detail)
+- [Summary](/builders/endpoints/summary)
+- [Errors](/builders/errors)
diff --git a/builders/endpoints/summary.mdx b/builders/endpoints/summary.mdx
new file mode 100644
index 0000000..b349bdf
--- /dev/null
+++ b/builders/endpoints/summary.mdx
@@ -0,0 +1,52 @@
+---
+title: "Summary"
+description: "Lightweight aggregate-only view of a referral code, ideal for dashboard widgets."
+openapi: "GET /builders/referrals/{code}/summary"
+---
+
+Returns the `summary` block of
+[`GET /referrals/{code}`](/builders/endpoints/list-referrals) without the
+full referral list. Use it when you only need tier counts and totals —
+payload is orders of magnitude smaller, making it safe to poll every few
+seconds.
+
+## Example request
+
+```bash
+curl https://services.junction.exchange/builders/referrals/ABC123/summary \
+ -H "x-api-key: $JXN_API_KEY"
+```
+
+## Example response
+
+```json
+{
+ "success": true,
+ "data": {
+ "referralCode": "ABC123",
+ "referralOwner": { "address": "0x…", "code": "ABC123" },
+ "summary": {
+ "tier1Count": 15,
+ "tier2Count": 8,
+ "tier3Count": 3,
+ "totalTier1Commission": 2450.75,
+ "totalTier2Commission": 245.30,
+ "totalTier3Commission": 48.15,
+ "totalVolume": 41200.12,
+ "totalCommission": 2744.20,
+ "commissionRates": { "tier1": 60, "tier2": 12, "tier3": 4 }
+ },
+ "generatedAt": "2026-04-18T12:34:56.000Z"
+ }
+}
+```
+
+
+This endpoint is Junction-specific — based.one does not expose an
+equivalent shortcut.
+
+
+## See also
+
+- [List referrals](/builders/endpoints/list-referrals)
+- [Commissions](/builders/commissions)
diff --git a/builders/endpoints/user-detail.mdx b/builders/endpoints/user-detail.mdx
new file mode 100644
index 0000000..8e92ae7
--- /dev/null
+++ b/builders/endpoints/user-detail.mdx
@@ -0,0 +1,121 @@
+---
+title: "Referred user detail"
+description: "Cumulative stats, asset-level breakdown, and Hyperliquid trade list for a single referred user."
+openapi: "GET /builders/referrals/{code}/user/{address}"
+---
+
+Returns detailed trading data for a single referred user (`address`) under
+referral code `code`. The `summary` block reflects the selected window
+(default 30 days); the `trades` array lists individual Hyperliquid fills
+within that window.
+
+## Example request
+
+
+```bash curl
+curl "https://services.junction.exchange/builders/referrals/ABC123/user/0xabc…?days=30" \
+ -H "x-api-key: $JXN_API_KEY"
+```
+
+```typescript TypeScript
+const qs = new URLSearchParams({ days: '30' })
+const res = await fetch(
+ `https://services.junction.exchange/builders/referrals/ABC123/user/0xabc…?${qs}`,
+ { headers: { 'x-api-key': process.env.JXN_API_KEY! } },
+)
+const body = await res.json()
+```
+
+```python Python
+import os, requests
+
+r = requests.get(
+ "https://services.junction.exchange/builders/referrals/ABC123/user/0xabc…",
+ params={"days": 30},
+ headers={"x-api-key": os.environ["JXN_API_KEY"]},
+ timeout=10,
+)
+body = r.json()
+```
+
+
+## Example response
+
+```json
+{
+ "success": true,
+ "data": {
+ "referralCode": "ABC123",
+ "referralOwner": { "address": "0x…", "code": "ABC123" },
+ "user": {
+ "address": "0xabcdef1234567890abcdef1234567890abcdef12",
+ "registrationDate": "2026-01-15T10:30:00.000Z",
+ "tier": 1,
+ "commissionRate": 60
+ },
+ "period": {
+ "type": "rolling_days",
+ "days": 30,
+ "startDate": "2026-03-19T12:34:56.000Z",
+ "endDate": "2026-04-18T12:34:56.000Z"
+ },
+ "summary": {
+ "totalVolume": 2450.75,
+ "totalBuilderFees": 1.2254,
+ "totalCommission": 0.7352,
+ "tradeCount": 15,
+ "assetBreakdown": {
+ "BTC": { "volume": 1500.50, "builderFees": 0.7503, "commission": 0.4502, "tradeCount": 8 },
+ "ETH": { "volume": 950.25, "builderFees": 0.4751, "commission": 0.2851, "tradeCount": 7 }
+ }
+ },
+ "trades": [
+ {
+ "source": "hyperliquid",
+ "tradeTime": "2026-04-18T01:30:00.000Z",
+ "volume": 250.50,
+ "builderFee": 0.1253,
+ "commissionEarned": 0.0752,
+ "coin": "BTC",
+ "side": "B"
+ }
+ ],
+ "meta": {
+ "notes": [
+ "Trades sourced from Hyperliquid `userFillsByTime` info endpoint.",
+ "Builder fees are estimated assuming the default Junction builder fee (0.05% perps)."
+ ],
+ "generatedAt": "2026-04-18T12:34:56.000Z"
+ }
+ }
+}
+```
+
+## Path parameters
+
+| Name | Format |
+| --- | --- |
+| `code` | 6-char `[A-Z0-9]{6}` referral code. |
+| `address` | 0x-prefixed 40-hex EVM address. |
+
+## Query parameters
+
+| Name | Type | Default | Description |
+| --- | --- | --- | --- |
+| `days` | integer | `30` | Window length (1 – 365). Ignored when `includeAllTime=true`. |
+| `includeAllTime` | boolean | `false` | Set to `true` to pull up to 365 days. |
+
+## Notes
+
+- `trades[].side` matches Hyperliquid's convention: `"B"` for buy/long,
+ `"A"` for sell/short.
+- `summary.totalBuilderFees` and `summary.totalCommission` are in USD and
+ rounded to 6 decimals; `totalVolume` is rounded to 2 decimals.
+- A successful response with an empty `trades` array means the user
+ exists under this code but had no Hyperliquid activity in the window.
+
+## Errors
+
+- `403 user_not_referral` — `address` is not a tier 1/2/3 of `code`.
+- `400 invalid_address` — malformed 0x address.
+- `400 invalid_referral_code` — malformed code.
diff --git a/builders/endpoints/volume-30d.mdx b/builders/endpoints/volume-30d.mdx
new file mode 100644
index 0000000..1ed377c
--- /dev/null
+++ b/builders/endpoints/volume-30d.mdx
@@ -0,0 +1,65 @@
+---
+title: "Referrals over 30 days"
+description: "Same shape as /referrals/{code} with a rolling 30-day `period` block."
+openapi: "GET /builders/referrals/volume_30d/{code}"
+---
+
+Returns the referral list in the same shape as
+[`GET /referrals/{code}`](/builders/endpoints/list-referrals) with an
+additional `period` block describing a rolling 30-day window.
+
+
+**Today's behaviour.** Junction's referral service currently materialises
+cumulative figures only. This endpoint returns the same totals as the
+all-time view with a `period.days = 30` stamp and surfaces the limitation
+in `meta.notes`. Period-scoped aggregation is on the roadmap. For
+**true 30-day data at the asset level**, use
+[`GET /referrals/{code}/user/{address}`](/builders/endpoints/user-detail)
+which reads directly from Hyperliquid's `userFillsByTime` stream.
+
+
+## Example request
+
+```bash
+curl "https://services.junction.exchange/builders/referrals/volume_30d/ABC123" \
+ -H "x-api-key: $JXN_API_KEY"
+```
+
+## Example response
+
+```json
+{
+ "success": true,
+ "data": {
+ "referralCode": "ABC123",
+ "referralOwner": { "address": "0x…", "code": "ABC123" },
+ "window": "30d",
+ "period": {
+ "startDate": "2026-03-19T12:34:56.000Z",
+ "endDate": "2026-04-18T12:34:56.000Z",
+ "days": 30
+ },
+ "referrals": [/* same shape as /referrals/{code} */],
+ "summary": { /* same shape */ },
+ "meta": {
+ "source": "referral-service",
+ "notes": [
+ "The Junction referral service currently materialises cumulative figures; the 30-day window returns the same totals until period-scoped aggregation ships.",
+ "Use /builders/referrals/:code/user/:address for true 30-day asset-level breakdown from Hyperliquid fills."
+ ],
+ "generatedAt": "2026-04-18T12:34:56.000Z"
+ }
+ }
+}
+```
+
+## Query parameters
+
+Same as [`GET /referrals/{code}`](/builders/endpoints/list-referrals):
+`sortBy`, `sortDirection`, `tier`.
+
+## See also
+
+- [List referrals](/builders/endpoints/list-referrals)
+- [User detail](/builders/endpoints/user-detail)
+- [Commissions](/builders/commissions)
diff --git a/builders/errors.mdx b/builders/errors.mdx
new file mode 100644
index 0000000..230757a
--- /dev/null
+++ b/builders/errors.mdx
@@ -0,0 +1,53 @@
+---
+title: "Errors"
+description: "Stable error codes returned by the Builders API."
+---
+
+All error responses follow the same envelope:
+
+```json
+{
+ "success": false,
+ "error": "Human-readable message",
+ "code": "machine_readable_code"
+}
+```
+
+Inspect `code` first — `error` is localized and may change without notice.
+
+## Codes
+
+| HTTP | `code` | Meaning | Suggested action |
+| --- | --- | --- | --- |
+| 400 | `invalid_referral_code` | `code` is not a valid 6-char `[A-Z0-9]{6}` string. | Validate input client-side before calling. |
+| 400 | `invalid_address` | `address` path param is not a valid 0x-prefixed 40-hex EVM address. | Check case and length. |
+| 401 | `invalid_api_key` | Missing, malformed, or expired `x-api-key`. | Rotate key via the contact form. |
+| 403 | `user_not_referral` | The address exists but is not a tier 1/2/3 of `code`. | Confirm the correct referral code. |
+| 404 | `referral_code_not_found` | The referral code is syntactically valid but unknown. | Confirm you typed your own code. |
+| 429 | *(no code, rate limiter returns plain text)* | Too many requests. | Read `Retry-After`; back off exponentially. |
+| 500 | `internal_error` | Unhandled server error. | Retry once; contact support if it persists. |
+| 502 | `upstream_unavailable` | Junction's referral service or Hyperliquid info endpoint is down. | Back off (5–30 s); retry with jitter. |
+
+## Retriable codes
+
+Errors marked retriable in other Junction APIs:
+
+- `internal_error` — retry once after a short delay.
+- `upstream_unavailable` — retry with exponential backoff and jitter.
+
+The rest (`invalid_*`, `user_not_referral`, `referral_code_not_found`) are
+**not** retriable — fix the input before retrying.
+
+## Example
+
+```bash
+curl -i "https://services.junction.exchange/builders/referrals/ABC123/user/0x0" \
+ -H "x-api-key: $JXN_API_KEY"
+```
+
+```http
+HTTP/2 400
+content-type: application/json
+
+{"success":false,"error":"Invalid address","code":"invalid_address"}
+```
diff --git a/builders/migrate-from-based.mdx b/builders/migrate-from-based.mdx
new file mode 100644
index 0000000..4cc8e70
--- /dev/null
+++ b/builders/migrate-from-based.mdx
@@ -0,0 +1,77 @@
+---
+title: "Migrate from based.one"
+description: "Drop-in migration path for integrations already targeting based.one's builder API."
+---
+
+Junction's Builders API was designed for **shape-compatibility** with
+based.one's `/api/builders/referrals/[code]` surface. Existing clients
+typically need a one-line change.
+
+## What stays the same
+
+- Header-based auth (`x-api-key`).
+- Path convention (`/referrals/{code}`, `/referrals/volume_30d/{code}`,
+ `/referrals/{code}/user/{address}`).
+- Query parameters (`sortBy`, `sortDirection`, `tier`).
+- Response envelope (`{ success, data: { referralCode, referralOwner,
+ referrals[], summary, … } }`).
+- Tier model and default commission rates (60% / 12% / 4%).
+
+## What's different
+
+| Capability | based.one | Junction |
+| --- | --- | --- |
+| Base URL | `https://app.based.one/api/builders/referrals` | `https://services.junction.exchange/builders/referrals` |
+| Summary-only endpoint | — | `GET /referrals/{code}/summary` |
+| OpenAPI 3.1 spec | — | [openapi.json](https://junction.exchange/openapi.json) (tag `builders`) |
+| MCP server tools for agents | — | [server-card.json](https://junction.exchange/.well-known/mcp/server-card.json) |
+| Scope | Hyperliquid only | Hyperliquid perps + spot **and** cross-chain LiFi swaps |
+| `volume_30d` freshness | Rolling 30-day aggregate | Cumulative with `period.days = 30` stamp today; period-scoped aggregation is on the roadmap (called out in `meta.notes`). |
+| Entry `referreeCode` | — | Exposed on every entry for audit / dedup. |
+| XP fields (`swapXp`, `referralXp`, `totalXp`) | — | Exposed on every entry. |
+
+## Migration checklist
+
+1. Replace the base URL:
+
+ ```diff
+ - https://app.based.one/api/builders/referrals
+ + https://services.junction.exchange/builders/referrals
+ ```
+
+2. Swap the API key for the Junction-issued one.
+3. Re-run your existing test suite — if you only read documented fields
+ (`address`, `tier`, `totalTraded`, `builderFees`, `commissionEarned`,
+ `commissionRate`, `registrationDate`), no code change is needed.
+4. If you relied on true 30-day figures for aggregate endpoints, move
+ those calculations to the per-user endpoint
+ (`/referrals/{code}/user/{address}?days=30`) which consumes the real
+ Hyperliquid fills stream.
+5. Optional: ingest the new XP fields into your dashboard if you surface
+ Junction XP to your users.
+
+## Side-by-side request
+
+```bash
+# based.one
+curl "https://app.based.one/api/builders/referrals/ABC123" \
+ -H "x-api-key: $BASED_KEY"
+
+# Junction — identical shape, different base URL
+curl "https://services.junction.exchange/builders/referrals/ABC123" \
+ -H "x-api-key: $JXN_KEY"
+```
+
+## Keep both in production
+
+Clients that want to stay multi-provider can keep the same request builder
+and switch the base URL via config:
+
+```typescript
+const BASE_URL = process.env.BUILDERS_PROVIDER === 'junction'
+ ? 'https://services.junction.exchange/builders'
+ : 'https://app.based.one/api/builders'
+```
+
+The returned `success`, `data.referrals[].*`, and `summary.*` fields are
+shape-compatible.
diff --git a/builders/openapi.json b/builders/openapi.json
new file mode 100644
index 0000000..05aeb73
--- /dev/null
+++ b/builders/openapi.json
@@ -0,0 +1,314 @@
+{
+ "openapi": "3.1.0",
+ "info": {
+ "title": "Junction Builders API",
+ "version": "1.0.0",
+ "summary": "Read-only referral analytics for Junction builder partners (based.one-compatible).",
+ "description": "Subset of https://junction.exchange/openapi.json filtered to the /builders/* paths. See https://junction.exchange/builders for the human reference.",
+ "contact": {
+ "name": "Junction Developer Support",
+ "email": "team@junction.exchange",
+ "url": "https://junction.exchange/contact?reason=builders"
+ }
+ },
+ "servers": [
+ { "url": "https://services.junction.exchange", "description": "Production" }
+ ],
+ "tags": [
+ { "name": "builders", "description": "Builder referral analytics (based.one-compatible)" }
+ ],
+ "security": [{ "builderApiKey": [] }],
+ "paths": {
+ "/builders/referrals/{code}": {
+ "get": {
+ "operationId": "buildersListReferrals",
+ "summary": "List all referrals (tiers 1/2/3) under a code",
+ "description": "Cumulative all-time view. Response is shape-compatible with based.one's /api/builders/referrals/[code] so existing integrations can reuse their client code by swapping the base URL.",
+ "tags": ["builders"],
+ "parameters": [
+ { "name": "code", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^[A-Z0-9]{6}$" } },
+ { "name": "sortBy", "in": "query", "schema": { "type": "string", "enum": ["registrationDate", "totalTraded", "commissionEarned"] } },
+ { "name": "sortDirection", "in": "query", "schema": { "type": "string", "enum": ["asc", "desc"] } },
+ { "name": "tier", "in": "query", "schema": { "type": "string", "enum": ["all", "tier1", "tier2", "tier3"] } }
+ ],
+ "responses": {
+ "200": {
+ "description": "Referrals envelope",
+ "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BuildersReferralsEnvelope" } } }
+ },
+ "400": { "description": "Invalid referral code", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BuildersError" } } } },
+ "401": { "description": "Missing or invalid API key", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BuildersError" } } } },
+ "404": { "description": "Referral code not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BuildersError" } } } },
+ "502": { "description": "Upstream referral service unavailable", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BuildersError" } } } }
+ }
+ }
+ },
+ "/builders/referrals/volume_30d/{code}": {
+ "get": {
+ "operationId": "buildersReferralsVolume30d",
+ "summary": "List referrals over a rolling 30-day window",
+ "description": "Currently returns the cumulative snapshot with a period.days = 30 stamp, pending period-scoped aggregation in the referral service. See `meta.notes`.",
+ "tags": ["builders"],
+ "parameters": [
+ { "name": "code", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^[A-Z0-9]{6}$" } },
+ { "name": "sortBy", "in": "query", "schema": { "type": "string", "enum": ["registrationDate", "totalTraded", "commissionEarned"] } },
+ { "name": "sortDirection", "in": "query", "schema": { "type": "string", "enum": ["asc", "desc"] } },
+ { "name": "tier", "in": "query", "schema": { "type": "string", "enum": ["all", "tier1", "tier2", "tier3"] } }
+ ],
+ "responses": {
+ "200": {
+ "description": "Referrals envelope with period block",
+ "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BuildersReferralsEnvelope" } } }
+ }
+ }
+ }
+ },
+ "/builders/referrals/{code}/user/{address}": {
+ "get": {
+ "operationId": "buildersReferralUser",
+ "summary": "Get detailed referral data for a specific user",
+ "description": "Returns the user's tier, cumulative stats, and a windowed Hyperliquid fills breakdown (by asset) plus individual trades.",
+ "tags": ["builders"],
+ "parameters": [
+ { "name": "code", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^[A-Z0-9]{6}$" } },
+ { "name": "address", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^0x[a-fA-F0-9]{40}$" } },
+ { "name": "days", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 365, "default": 30 } },
+ { "name": "includeAllTime", "in": "query", "schema": { "type": "boolean" } }
+ ],
+ "responses": {
+ "200": {
+ "description": "User referral detail envelope",
+ "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BuildersUserEnvelope" } } }
+ },
+ "403": { "description": "User is not a referral under this code", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BuildersError" } } } }
+ }
+ }
+ },
+ "/builders/referrals/{code}/summary": {
+ "get": {
+ "operationId": "buildersReferralSummary",
+ "summary": "Aggregated summary only, no entry list",
+ "description": "Lightweight endpoint for dashboards. Returns tier counts, totals, and commission rates without the full referrals list.",
+ "tags": ["builders"],
+ "parameters": [
+ { "name": "code", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^[A-Z0-9]{6}$" } }
+ ],
+ "responses": {
+ "200": {
+ "description": "Summary envelope",
+ "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BuildersSummaryEnvelope" } } }
+ }
+ }
+ }
+ }
+ },
+ "components": {
+ "securitySchemes": {
+ "builderApiKey": {
+ "type": "apiKey",
+ "in": "header",
+ "name": "x-api-key",
+ "description": "Builder API key issued via https://junction.exchange/builders. Same value also accepted on the `X-Junction-Api-Key` header."
+ }
+ },
+ "schemas": {
+ "BuildersReferralOwner": {
+ "type": "object",
+ "required": ["address", "code"],
+ "properties": {
+ "address": { "type": "string" },
+ "code": { "type": "string" }
+ }
+ },
+ "BuildersReferralEntry": {
+ "type": "object",
+ "required": [
+ "address",
+ "registrationDate",
+ "tier",
+ "totalTraded",
+ "builderFees",
+ "commissionEarned",
+ "commissionRate"
+ ],
+ "properties": {
+ "address": { "type": "string" },
+ "registrationDate": { "type": "string", "format": "date-time" },
+ "tier": { "type": "integer", "enum": [1, 2, 3] },
+ "totalTraded": { "type": "number", "description": "Cumulative USD volume." },
+ "builderFees": { "type": "number", "description": "Cumulative builder fees paid by this user (USD)." },
+ "commissionEarned": { "type": "number" },
+ "commissionRate": { "type": "number", "description": "Percentage, e.g. 60 = 60% of builder fees." },
+ "referreeCode": { "type": ["string", "null"] },
+ "swapXp": { "type": "number" },
+ "referralXp": { "type": "number" },
+ "totalXp": { "type": "number" }
+ }
+ },
+ "BuildersReferralSummary": {
+ "type": "object",
+ "required": ["tier1Count", "tier2Count", "tier3Count", "totalVolume", "totalCommission", "commissionRates"],
+ "properties": {
+ "tier1Count": { "type": "integer" },
+ "tier2Count": { "type": "integer" },
+ "tier3Count": { "type": "integer" },
+ "totalTier1Commission": { "type": "number" },
+ "totalTier2Commission": { "type": "number" },
+ "totalTier3Commission": { "type": "number" },
+ "totalVolume": { "type": "number" },
+ "totalCommission": { "type": "number" },
+ "commissionRates": {
+ "type": "object",
+ "properties": {
+ "tier1": { "type": "number" },
+ "tier2": { "type": "number" },
+ "tier3": { "type": "number" }
+ }
+ }
+ }
+ },
+ "BuildersReferralsEnvelope": {
+ "type": "object",
+ "required": ["success", "data"],
+ "properties": {
+ "success": { "type": "boolean", "enum": [true] },
+ "data": {
+ "type": "object",
+ "required": ["referralCode", "referralOwner", "window", "referrals", "summary"],
+ "properties": {
+ "referralCode": { "type": "string" },
+ "referralOwner": { "$ref": "#/components/schemas/BuildersReferralOwner" },
+ "window": { "type": "string", "enum": ["cumulative", "30d"] },
+ "period": {
+ "type": "object",
+ "properties": {
+ "startDate": { "type": "string", "format": "date-time" },
+ "endDate": { "type": "string", "format": "date-time" },
+ "days": { "type": "integer" }
+ }
+ },
+ "referrals": {
+ "type": "array",
+ "items": { "$ref": "#/components/schemas/BuildersReferralEntry" }
+ },
+ "summary": { "$ref": "#/components/schemas/BuildersReferralSummary" },
+ "meta": {
+ "type": "object",
+ "properties": {
+ "source": { "type": "string" },
+ "notes": { "type": "array", "items": { "type": "string" } },
+ "generatedAt": { "type": "string", "format": "date-time" }
+ }
+ }
+ }
+ }
+ }
+ },
+ "BuildersUserEnvelope": {
+ "type": "object",
+ "required": ["success", "data"],
+ "properties": {
+ "success": { "type": "boolean", "enum": [true] },
+ "data": {
+ "type": "object",
+ "required": ["referralCode", "referralOwner", "user", "period", "summary", "trades"],
+ "properties": {
+ "referralCode": { "type": "string" },
+ "referralOwner": { "$ref": "#/components/schemas/BuildersReferralOwner" },
+ "user": {
+ "type": "object",
+ "properties": {
+ "address": { "type": "string" },
+ "registrationDate": { "type": "string", "format": "date-time" },
+ "tier": { "type": "integer", "enum": [1, 2, 3] },
+ "commissionRate": { "type": "number" }
+ }
+ },
+ "period": {
+ "type": "object",
+ "properties": {
+ "type": { "type": "string", "enum": ["rolling_days", "all_time"] },
+ "days": { "type": "integer" },
+ "startDate": { "type": "string", "format": "date-time" },
+ "endDate": { "type": "string", "format": "date-time" }
+ }
+ },
+ "summary": {
+ "type": "object",
+ "properties": {
+ "totalVolume": { "type": "number" },
+ "totalBuilderFees": { "type": "number" },
+ "totalCommission": { "type": "number" },
+ "tradeCount": { "type": "integer" },
+ "assetBreakdown": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "object",
+ "properties": {
+ "volume": { "type": "number" },
+ "builderFees": { "type": "number" },
+ "commission": { "type": "number" },
+ "tradeCount": { "type": "integer" }
+ }
+ }
+ }
+ }
+ },
+ "trades": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "source": { "type": "string", "enum": ["hyperliquid"] },
+ "tradeTime": { "type": "string", "format": "date-time" },
+ "volume": { "type": "number" },
+ "builderFee": { "type": "number" },
+ "commissionEarned": { "type": "number" },
+ "coin": { "type": "string" },
+ "side": { "type": "string", "enum": ["B", "A"] }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "BuildersSummaryEnvelope": {
+ "type": "object",
+ "required": ["success", "data"],
+ "properties": {
+ "success": { "type": "boolean", "enum": [true] },
+ "data": {
+ "type": "object",
+ "properties": {
+ "referralCode": { "type": "string" },
+ "referralOwner": { "$ref": "#/components/schemas/BuildersReferralOwner" },
+ "summary": { "$ref": "#/components/schemas/BuildersReferralSummary" },
+ "generatedAt": { "type": "string", "format": "date-time" }
+ }
+ }
+ }
+ },
+ "BuildersError": {
+ "type": "object",
+ "required": ["success", "error", "code"],
+ "properties": {
+ "success": { "type": "boolean", "enum": [false] },
+ "error": { "type": "string" },
+ "code": {
+ "type": "string",
+ "enum": [
+ "invalid_api_key",
+ "invalid_referral_code",
+ "invalid_address",
+ "referral_code_not_found",
+ "user_not_referral",
+ "upstream_unavailable",
+ "internal_error"
+ ]
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/builders/overview.mdx b/builders/overview.mdx
new file mode 100644
index 0000000..b2b3dce
--- /dev/null
+++ b/builders/overview.mdx
@@ -0,0 +1,106 @@
+---
+title: "Builders API"
+description: "Read-only REST API for partners who route users to Junction and track their referral tree, volume, and commissions."
+---
+
+The **Builders API** lets partner builders and AI agents track the referrals
+acquired through a Junction referral code. It is read-only, authenticated
+with a single `x-api-key` header, and response-compatible with
+based.one — integrations that already target based can switch to Junction
+by swapping only the base URL.
+
+## Base URL
+
+```
+https://services.junction.exchange/builders
+```
+
+## What you can do
+
+- List every user who signed up with your referral code (**tier 1**), plus
+ their downstream (**tier 2** and **tier 3**).
+- Fetch cumulative trading volume, builder fees paid, and your commission
+ earned per referred user.
+- Drill into a single referred user for an asset-level breakdown and a
+ trade list from Hyperliquid (rolling window up to 365 days).
+- Pull a lightweight aggregate summary for dashboards.
+
+## Endpoints
+
+
+
+ `GET /referrals/{code}` — all tiers, cumulative.
+
+
+ `GET /referrals/volume_30d/{code}` — rolling 30-day view.
+
+
+ `GET /referrals/{code}/user/{address}` — per-user breakdown + trades.
+
+
+ `GET /referrals/{code}/summary` — aggregate only.
+
+
+
+## Response envelope
+
+Every successful response is wrapped in `{ success: true, data: ... }`.
+Errors return `{ success: false, error, code }`.
+
+```json
+{
+ "success": true,
+ "data": {
+ "referralCode": "ABC123",
+ "referralOwner": { "address": "0x…", "code": "ABC123" },
+ "window": "cumulative",
+ "referrals": [
+ {
+ "address": "0x…",
+ "registrationDate": "2026-01-15T10:30:00.000Z",
+ "tier": 1,
+ "totalTraded": 15750.25,
+ "builderFees": 7.87,
+ "commissionEarned": 4.72,
+ "commissionRate": 60
+ }
+ ],
+ "summary": { "tier1Count": 1, "totalCommission": 4.72 },
+ "meta": {
+ "source": "referral-service",
+ "notes": ["Volume and commission figures are cumulative (all-time)."],
+ "generatedAt": "2026-04-18T12:34:56.000Z"
+ }
+ }
+}
+```
+
+
+The `window` field is always `"cumulative"` on `/referrals/{code}` and
+`"30d"` on `/referrals/volume_30d/{code}`. The per-user endpoint exposes a
+`period` block with exact `startDate` / `endDate` for the window you asked
+for.
+
+
+## Related
+
+
+
+ 5-minute curl / TypeScript walkthrough.
+
+
+ How API keys work and where to request one.
+
+
+ Tier 1 / 2 / 3 rates and how they are computed.
+
+
+ Error codes and suggested retries.
+
+
+ What to change in your existing client.
+
+
+ Full OpenAPI 3.1 document (tag `builders`).
+
+
diff --git a/builders/quickstart.mdx b/builders/quickstart.mdx
new file mode 100644
index 0000000..dcb0e38
--- /dev/null
+++ b/builders/quickstart.mdx
@@ -0,0 +1,81 @@
+---
+title: "Quickstart"
+description: "Call the Builders API in under five minutes."
+---
+
+## 1. Get an API key
+
+Request a Builders API key via the
+[contact form](https://junction.exchange/contact?reason=builders) or email
+[team@junction.exchange](mailto:team@junction.exchange).
+
+The key is a single opaque string. Keep it server-side — do not ship it to
+browsers.
+
+## 2. Make your first request
+
+Replace `ABC123` with your referral code and `$JXN_API_KEY` with the key
+you just received.
+
+
+```bash curl
+curl https://services.junction.exchange/builders/referrals/ABC123 \
+ -H "x-api-key: $JXN_API_KEY"
+```
+
+```typescript TypeScript
+const res = await fetch(
+ 'https://services.junction.exchange/builders/referrals/ABC123',
+ { headers: { 'x-api-key': process.env.JXN_API_KEY! } },
+)
+const { success, data } = await res.json()
+if (!success) throw new Error(data?.error ?? 'request failed')
+console.log(data.summary.totalCommission)
+```
+
+```python Python
+import os, requests
+
+r = requests.get(
+ "https://services.junction.exchange/builders/referrals/ABC123",
+ headers={"x-api-key": os.environ["JXN_API_KEY"]},
+ timeout=10,
+)
+r.raise_for_status()
+body = r.json()
+print(body["data"]["summary"]["totalCommission"])
+```
+
+
+## 3. Read a single referred user
+
+Pull the last 30 days of Hyperliquid activity for a specific wallet under
+your code:
+
+```bash
+curl "https://services.junction.exchange/builders/referrals/ABC123/user/0xabc…?days=30" \
+ -H "x-api-key: $JXN_API_KEY"
+```
+
+The response includes a per-asset breakdown under
+`data.summary.assetBreakdown` and individual trades under `data.trades`.
+
+## 4. Wire it into a dashboard
+
+For lightweight widgets use the summary endpoint:
+
+```bash
+curl https://services.junction.exchange/builders/referrals/ABC123/summary \
+ -H "x-api-key: $JXN_API_KEY"
+```
+
+It returns only the aggregate `{ tier1Count, totalCommission, commissionRates, … }`
+block without the full referral list, so it stays fast on dashboards that
+poll every few seconds.
+
+## 5. Handle errors and rate limits
+
+Every response includes `X-RateLimit-Limit` and `X-RateLimit-Remaining`
+headers. On `429` inspect `Retry-After`. Errors are always
+`{ success: false, error, code }` — see [Errors](/builders/errors) for the
+list of stable codes.
diff --git a/mint.json b/mint.json
index 0a89c2e..1cae815 100644
--- a/mint.json
+++ b/mint.json
@@ -33,11 +33,18 @@
"name": "API Reference",
"url": "api-reference"
},
+ {
+ "name": "Builders API",
+ "url": "builders"
+ },
{
"name": "Integrations",
"url": "integrations"
}
],
+ "openapi": [
+ "builders/openapi.json"
+ ],
"navigation": [
{
"group": "Getting started",
@@ -94,6 +101,26 @@
"api-reference/endpoint/graphql"
]
},
+ {
+ "group": "Builders API",
+ "pages": [
+ "builders/overview",
+ "builders/quickstart",
+ "builders/authentication",
+ "builders/commissions",
+ "builders/errors",
+ "builders/migrate-from-based",
+ {
+ "group": "Endpoints",
+ "pages": [
+ "builders/endpoints/list-referrals",
+ "builders/endpoints/volume-30d",
+ "builders/endpoints/user-detail",
+ "builders/endpoints/summary"
+ ]
+ }
+ ]
+ },
{
"group": "Integrations",
"pages": [