From 8d92c7ace4f4c53b6d75f83d287b452bd469ed43 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 20 Apr 2026 14:09:11 +0000 Subject: [PATCH 1/2] docs(builders): add Mintlify MDX pack for the Builders API Port the Builders API Mintlify pack from junction-front-end#417 into this docs repo (docs.junction.exchange): - builders/overview.mdx - builders/quickstart.mdx - builders/authentication.mdx - builders/commissions.mdx - builders/errors.mdx - builders/migrate-from-based.mdx - builders/endpoints/list-referrals.mdx - builders/endpoints/volume-30d.mdx - builders/endpoints/user-detail.mdx - builders/endpoints/summary.mdx - builders/openapi.json (version-pinned subset of junction.exchange/openapi.json) Navigation wiring in mint.json comes in a follow-up commit. Co-authored-by: Zdeadex --- builders/authentication.mdx | 69 ++++++ builders/commissions.mdx | 62 +++++ builders/endpoints/list-referrals.mdx | 109 +++++++++ builders/endpoints/summary.mdx | 52 +++++ builders/endpoints/user-detail.mdx | 121 ++++++++++ builders/endpoints/volume-30d.mdx | 65 ++++++ builders/errors.mdx | 53 +++++ builders/migrate-from-based.mdx | 77 +++++++ builders/openapi.json | 314 ++++++++++++++++++++++++++ builders/overview.mdx | 106 +++++++++ builders/quickstart.mdx | 81 +++++++ 11 files changed, 1109 insertions(+) create mode 100644 builders/authentication.mdx create mode 100644 builders/commissions.mdx create mode 100644 builders/endpoints/list-referrals.mdx create mode 100644 builders/endpoints/summary.mdx create mode 100644 builders/endpoints/user-detail.mdx create mode 100644 builders/endpoints/volume-30d.mdx create mode 100644 builders/errors.mdx create mode 100644 builders/migrate-from-based.mdx create mode 100644 builders/openapi.json create mode 100644 builders/overview.mdx create mode 100644 builders/quickstart.mdx 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. From 60c7e403a67e905e83405272fd34fc801f23a1cb Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 20 Apr 2026 14:10:05 +0000 Subject: [PATCH 2/2] docs(builders): wire Builders API into mint.json navigation - Add 'Builders API' top tab pointing to builders/overview. - Add a 'Builders API' navigation group with an Endpoints sub-group. - Register builders/openapi.json so Mintlify auto-renders REST request/response forms on endpoint pages that declare `openapi: "GET /builders/..."` in their frontmatter. Co-authored-by: Zdeadex --- mint.json | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) 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": [