# Riddle developer docs

Canonical hosts from hub catalog. Hub: https://riddlewallet.com

- Guide model: **grok-4.6** (`GUIDE_MODEL`). `XAI_API_KEY` is server-only. HTTP 503 if the key is missing. Never ship the key to the browser.
- Suite SSO cookie: `rdl_sess` on `Domain=.riddlewallet.com`. Xaman SignIn is a separate identity.
- Credits: 1 cr = $0.01; **100 cr = $1**. Credits ≠ Bronze/Silver/Gold fee tiers.
- Send platform fee: **0**. Swap 0.85% → 0.1% Gold. Bridge 1.2% → 0.5%. Cafe 1.0% → 0.4%.
- Units: integer drops / lamports / wei. Never `float * 1e6`.
- Do not put seeds, PINs, private keys, or API secrets in URLs, cookies, ads payloads, or Guide messages.

---

## Getting started

slug: getting-started

Create or import a self-custodial wallet, connect the suite once, then open any Riddle app without a second seed.

# Getting started

This hub is the public front door for the Riddle suite ([https://riddlewallet.com](https://riddlewallet.com)). Keys stay on the device in [Riddle Wallet](https://wallet.riddlewallet.com). The hub never holds seeds.

## 1. Open Wallet

Go to [https://wallet.riddlewallet.com](https://wallet.riddlewallet.com). Create a vault or import an existing seed. Set a local PIN. Write the backup offline.

Hub, Guide, support, and chat never receive seeds. Anyone with the backup controls funds.

## 2. Connect the suite (SSO)

Other Riddle apps do **not** ask you to import the seed again. They open Wallet with:

```
https://wallet.riddlewallet.com?return=&app=&action=connect
```

Set `return` to the app URL (must be `riddlewallet.com` or `*.riddlewallet.com`), `app` to the product id (`hub`, `swap`, `bridge`, …), and `action=connect`.

Wallet unlocks, writes cookie `rdl_sess` with `Domain=.riddlewallet.com`, and may `postMessage` type `riddle-wallet:connected`. The opener stores the address session and returns. **No second seed import.**

Xaman SignIn is a **different** identity system. See [Xaman](/docs/xaman).

## 3. Sign after idle

Browse freely. After **1 hour** idle (`REAUTH_IDLE_MS` = 3600000), PIN or swipe again before send / swap / bridge. See [payloads](/docs/payloads).

## 4. Units, fees, credits

- Amounts are integer **drops / lamports / wei** — never `float * 1e6`.
- Platform send fee is **0**. Swap / bridge / Cafe bps follow Free → Gold. See [fees & credits](/docs/fees-credits).
- **1 credit = $0.01** (100 cr = $1). Credits power Studio, kits, ads, and games. They are **not** fee tiers.

## Next

- [Suite map](/docs/suite-map) — every production host from catalog `URLS` / `APPS`
- [Deep links](/docs/deep-links)
- [OpenAPI](/docs/openapi)
- Machine copy: [https://riddlewallet.com/docs.md](/docs.md) · [https://riddlewallet.com/llms.txt](/llms.txt)

---

## Suite map

slug: suite-map

Canonical production hosts from the hub catalog — one SSO cookie across *.riddlewallet.com.

# Suite map

All live hosts below come from hub `URLS` / `APPS` in `src/lib/catalog.ts`. Do not invent subdomains.

## Canonical URLS

| Key | URL |
| --- | --- |
| `hub` | https://riddlewallet.com |
| `wallet` | https://wallet.riddlewallet.com |
| `swap` | https://swap.riddlewallet.com |
| `bridge` | https://bridge.riddlewallet.com |
| `scanner` | https://scanner.riddlewallet.com |
| `cafe` | https://cafe.riddlewallet.com |
| `creator` | https://dev.riddlewallet.com |
| `dev` | https://dev.riddlewallet.com |
| `social` | https://social.riddlewallet.com |
| `pay` | https://pay.riddlewallet.com |
| `game` | https://game.riddlewallet.com |
| `fighter` | https://fighter.riddlewallet.com |
| `cities` | https://cities.riddlewallet.com |
| `sniper` | https://sniper.riddlewallet.com |
| `loyalty` | https://loyalty.riddlewallet.com |
| `partners` | https://partners.riddlewallet.com |
| `account` | https://account.riddlewallet.com |
| `credits` | https://wallet.riddlewallet.com/?tab=credits |
| `plans` | https://wallet.riddlewallet.com/?tab=subscriptions |

Apex hub: [https://riddlewallet.com](https://riddlewallet.com).

## Apps

| App | Slug | Host | Group |
| --- | --- | --- | --- |
| [Riddle Wallet](https://wallet.riddlewallet.com) | `wallet` | `wallet.riddlewallet.com` | Home |
| [Riddle Swap](https://swap.riddlewallet.com) | `swap` | `swap.riddlewallet.com` | Market |
| [Riddle Bridge](https://bridge.riddlewallet.com) | `bridge` | `bridge.riddlewallet.com` | Market |
| [Riddle Scanner](https://scanner.riddlewallet.com) | `scanner` | `scanner.riddlewallet.com` | Market |
| [Riddle Cafe](https://cafe.riddlewallet.com) | `cafe` | `cafe.riddlewallet.com` | Market |
| [Riddle Creator](https://dev.riddlewallet.com) | `creator` | `dev.riddlewallet.com` | Create |
| [Riddle Dev](https://dev.riddlewallet.com) | `dev` | `dev.riddlewallet.com` | Create |
| [Riddle Social](https://social.riddlewallet.com) | `social` | `social.riddlewallet.com` | Platform |
| [Riddle Pay](https://pay.riddlewallet.com) | `pay` | `pay.riddlewallet.com` | Platform |
| [Riddle Game](https://game.riddlewallet.com) | `game` | `game.riddlewallet.com` | Games |
| [Fighter](https://fighter.riddlewallet.com) | `fighter` | `fighter.riddlewallet.com` | Games |
| [Cities](https://cities.riddlewallet.com) | `cities` | `cities.riddlewallet.com` | Games |
| [Sniper](https://sniper.riddlewallet.com) | `sniper` | `sniper.riddlewallet.com` | Market |
| [Loyalty](https://loyalty.riddlewallet.com) | `loyalty` | `loyalty.riddlewallet.com` | Create |
| [Partners](https://partners.riddlewallet.com) | `partners` | `partners.riddlewallet.com` | Platform |
| [Account](https://account.riddlewallet.com) | `account` | `account.riddlewallet.com` | Platform |

## Groups

- **Home** (`home`): Riddle Wallet
- **Market** (`market`): Riddle Swap, Riddle Bridge, Riddle Scanner, Riddle Cafe, Sniper
- **Games** (`world`): Riddle Game, Fighter, Cities
- **Create** (`growth`): Riddle Creator, Riddle Dev, Loyalty
- **Platform** (`platform`): Riddle Social, Riddle Pay, Partners, Account

## What is not a wallet

- [Account](https://account.riddlewallet.com) is privacy / consent — **not** the keystore.
- Hub screenshots and marketing pages never hold keys.
- Game surfaces ([Game](https://game.riddlewallet.com), [Fight](https://fighter.riddlewallet.com), [Cities](https://cities.riddlewallet.com)) spend **suite credits**, not a second seed.

## Related hub routes

| Path | Purpose |
| --- | --- |
| `/apps` · `/apps/{slug}` | App store pages |
| `/features` · `/features/{slug}` | How-to features |
| `/docs` · `/docs/{slug}` | This portal |
| `/start` `/guide` `/fees` `/credits` `/subscriptions` | Product explainers |
| `/security` `/status` `/changelog` `/about` `/contact` | Trust + company |

---

## Suite SSO

slug: sso

Login once in Wallet. Cookie rdl_sess on .riddlewallet.com follows you. No second seed import.

# Suite SSO

Suite SSO is **Riddle Wallet** identity. It is not Xaman SignIn.

## Connect URL

Open Wallet with query params (order does not matter):

```
https://wallet.riddlewallet.com?return=&app=&action=connect
```

| Param | Required | Notes |
| --- | --- | --- |
| `action` | yes | `connect` (also `sign` / `pay` for payloads) |
| `app` | yes | Suite id: `hub`, `swap`, `bridge`, `cafe`, … |
| `return` | yes | Full URL on `riddlewallet.com` or `*.riddlewallet.com` (localhost allowed in dev) |
| `source` | optional | Hub chrome sends `source=suite` |
| `chain` | optional | Hint (`xrpl`, `evm`, `solana`, `stellar`) |

Example — hub connect:

```
https://wallet.riddlewallet.com?app=hub&action=connect&return=https%3A%2F%2Friddlewallet.com%2F
```

## Cookie

| Name | Domain | Role |
| --- | --- | --- |
| `rdl_sess` | `.riddlewallet.com` | Suite identity session (address + SSO). Shared across apps. |

Do **not** treat `rdl_sess` as a signing unlock. Signing is gated by local PIN / swipe and idle re-auth.

Identity TTL is separate from re-auth. **Re-auth idle default is 1 hour** (`REAUTH_IDLE_MS` = 3600000). After idle, the address session can still be present; the user must unlock again before send / swap / bridge.

## postMessage

Wallet may notify the opener:

```json
{
  "type": "riddle-wallet:connected",
  "address": "r…",
  "chain": "xrpl",
  "source": "riddle-wallet"
}
```

Listen only for `event.origin` equal to the Wallet origin ([https://wallet.riddlewallet.com](https://wallet.riddlewallet.com)). Store `riddle_wallet_session` in `localStorage` on the app origin.

Related types (do not mix with Xaman): `riddle-wallet:disconnected`, `riddle-wallet:ready`, `riddle-wallet:signed`, `riddle-wallet:session-changed`.

## Rules

1. **No second seed import** on Swap, Bridge, Cafe, Dev, games, or Hub.
2. `return=` is allow-listed. Reject open redirects.
3. Never put seeds, PINs, or private keys in query strings, cookies, or postMessage.
4. **Xaman SignIn is a separate identity system.** Do not copy Xaman tokens into `rdl_sess`.

Full walkthrough: [Getting started](/docs/getting-started) · [Xaman](/docs/xaman) · [Deep links](/docs/deep-links).

---

## Deep links

slug: deep-links

Connect, credits, plans, app hosts, and hub feature URLs — always production catalog hosts.

# Deep links

Use catalog hosts only. Path-only links like `/wallet` on the hub **redirect** to the Wallet host; prefer the absolute URL.

## SSO connect

```
https://wallet.riddlewallet.com?return={absoluteAppUrl}&app={id}&action=connect
```

See [SSO](/docs/sso).

## Wallet surfaces

| Intent | URL |
| --- | --- |
| Wallet home | [https://wallet.riddlewallet.com](https://wallet.riddlewallet.com) |
| Credits | [https://wallet.riddlewallet.com/?tab=credits](https://wallet.riddlewallet.com/?tab=credits) |
| Plans / tiers | [https://wallet.riddlewallet.com/?tab=subscriptions](https://wallet.riddlewallet.com/?tab=subscriptions) |
| Connect | `https://wallet.riddlewallet.com?return=&app=&action=connect` |
| Sign (suite) | `https://wallet.riddlewallet.com?action=sign&tx=&return=&app=` |
| Pay (suite) | `https://wallet.riddlewallet.com?action=pay&to=&amount=&return=&app=` |

`amount` on pay/sign paths must be **native integer units** (drops / lamports / wei). See [payloads](/docs/payloads).

## App homes

- **Riddle Wallet** — [https://wallet.riddlewallet.com](https://wallet.riddlewallet.com)
- **Riddle Swap** — [https://swap.riddlewallet.com](https://swap.riddlewallet.com)
- **Riddle Bridge** — [https://bridge.riddlewallet.com](https://bridge.riddlewallet.com)
- **Riddle Scanner** — [https://scanner.riddlewallet.com](https://scanner.riddlewallet.com)
- **Riddle Cafe** — [https://cafe.riddlewallet.com](https://cafe.riddlewallet.com)
- **Riddle Creator** — [https://dev.riddlewallet.com](https://dev.riddlewallet.com)
- **Riddle Dev** — [https://dev.riddlewallet.com](https://dev.riddlewallet.com)
- **Riddle Social** — [https://social.riddlewallet.com](https://social.riddlewallet.com)
- **Riddle Pay** — [https://pay.riddlewallet.com](https://pay.riddlewallet.com)
- **Riddle Game** — [https://game.riddlewallet.com](https://game.riddlewallet.com)
- **Fighter** — [https://fighter.riddlewallet.com](https://fighter.riddlewallet.com)
- **Cities** — [https://cities.riddlewallet.com](https://cities.riddlewallet.com)
- **Sniper** — [https://sniper.riddlewallet.com](https://sniper.riddlewallet.com)
- **Loyalty** — [https://loyalty.riddlewallet.com](https://loyalty.riddlewallet.com)
- **Partners** — [https://partners.riddlewallet.com](https://partners.riddlewallet.com)
- **Account** — [https://account.riddlewallet.com](https://account.riddlewallet.com)

## Hub pages

| Page | URL |
| --- | --- |
| Hub | [https://riddlewallet.com](https://riddlewallet.com) |
| Apps index | [https://riddlewallet.com/apps](https://riddlewallet.com/apps) |
| App page | `https://riddlewallet.com/apps/{slug}` (slug from catalog) |
| Features | [https://riddlewallet.com/features](https://riddlewallet.com/features) |
| Feature page | `https://riddlewallet.com/features/{slug}` |
| Docs | [https://riddlewallet.com/docs](https://riddlewallet.com/docs) |
| Start | [https://riddlewallet.com/start](https://riddlewallet.com/start) |
| Guide | [https://riddlewallet.com/guide](https://riddlewallet.com/guide) |
| Fees | [https://riddlewallet.com/fees](https://riddlewallet.com/fees) |
| Credits | [https://riddlewallet.com/credits](https://riddlewallet.com/credits) |
| Subscriptions | [https://riddlewallet.com/subscriptions](https://riddlewallet.com/subscriptions) |

SEO landings: `https://riddlewallet.com/go/bridge/{slug}`, `https://riddlewallet.com/go/swap/{slug}` (crawl allowed).

## Query rules

- Never put a seed, PIN, or private key in a URL.
- `return` must stay on `*.riddlewallet.com`.
- Prefer `encodeURIComponent` for `return`.

---

## Payloads & units

slug: payloads

Build amounts in drops, lamports, or wei. Never float × 1e6. Warn, then re-auth after idle.

# Payloads

Every sign path (Wallet send, Swap, Bridge, Cafe, suite `action=sign|pay`) must show **network, amount, destination**, and irreversible copy before the user signs.

## Native units only

| Family | Unit | Integer example |
| --- | --- | --- |
| XRPL | drops | `1000000` drops = 1 XRP |
| Solana | lamports | `1000000000` lamports = 1 SOL |
| EVM | wei | `1000000000000000000` wei = 1 ETH |

**Never** do `amountFloat * 1e6` (or `* 1e18`) in IEEE float. Convert with integer math or a decimal library that emits a string/bigint of whole units.

```
# BAD — float rounding
(0.1 * 1e6).toString()

# GOOD — integer drops / lamports / wei
drops = 100000n
```

Pass those integers on Wallet pay/sign deep links as decimal strings, not JSON floats.

## Warning modal (required)

Before sign:

1. Chain / network label
2. Amount in native units **and** a display string
3. Destination (truncated + copy) or offer/NFT id
4. Platform fee bps when the product takes a cut
5. Irreversible warning
6. Wrong-chain / memo-required copy when relevant

## Re-auth

Default idle: **1 hour** (`REAUTH_IDLE_MS` = 3600000).

If the user has been idle ≥ 1 hour (or tapped Lock), require PIN / swipe **again** even if `rdl_sess` is still set. SSO cookie ≠ signing unlock.

## Never

- Log seeds, PINs, or raw keystore dumps
- Put keys in URLs, cookies, or ads payloads
- Skip the warning modal for “small” amounts
- Mix Xaman payload UUIDs with suite `rdl_sess`

---

## Fees & credits

slug: fees-credits

Swap 0.85%→0.1% Gold, bridge 1.2%→0.5%, cafe 1.0%→0.4%, send 0. Credits are $0.01 each — not fee tiers.

# Fees and credits

Two different ledgers. Do not conflate them.

## Platform fees (bps → percent)

| Product | Free | Bronze | Silver | Gold |
| --- | --- | --- | --- | --- |
| Swap | 0.85% | 0.7% | 0.35% | 0.1% |
| Bridge | 1.2% | 1% | 0.8% | 0.5% |
| Cafe | 1% | 0.8% | 0.6% | 0.4% |
| Send | 0% | 0% | 0% | 0% |

- **Swap:** Free **0.85%** · Gold **0.1%**
- **Bridge:** **1.2% → 0.5%** Gold
- **Cafe:** **1.0% → 0.4%** Gold
- **Send:** platform fee **0** on every tier (network fees still apply)

On-chain ledger / gas is paid to the network, not to Riddle.

## Wallet plans (fee tiers)

| Plan | Price |
| --- | --- |
| Free | $0 |
| Bronze | $4.99 / mo |
| Silver | $14.99 / mo |
| Gold | $39.99 / mo |

Plans live at [https://wallet.riddlewallet.com/?tab=subscriptions](https://wallet.riddlewallet.com/?tab=subscriptions). They **cut swap / bridge / Cafe bps**. They do **not** grant Studio credits.

## Suite credits

| | |
| --- | --- |
| 1 credit | **$0.01** |
| 100 credits | **$1** |
| Top-up | [https://wallet.riddlewallet.com/?tab=credits](https://wallet.riddlewallet.com/?tab=credits) and [Dev](https://dev.riddlewallet.com) |

Credits power Dev studio, kits, ad auctions (paid in XRP for slots), and game economies.

**Credits ≠ fee tiers.** Buying Gold does not mint credits. Buying a credit pack does not change swap bps.

PAYG SKUs (memo `RDL1|PAYG|…`) apply when a Dev quota is exhausted — still credits / packs, not Bronze/Silver/Gold.

---

## Ads

slug: ads

Fetch live creatives with GET {ADS_BASE}/api/ads/active?slot= — auctions settle in XRP on Dev.

# Ads

Ad inventory is served by **Riddle Dev**. Hub and Wallet never invent third-party sponsors.

## Endpoint

```
GET https://dev.riddlewallet.com/api/ads/active?slot=
```

`ADS_BASE` defaults to [https://dev.riddlewallet.com](https://dev.riddlewallet.com) (`URLS.dev`). Apps may override with `NEXT_PUBLIC_ADS_API_BASE`.

| Query | Behavior |
| --- | --- |
| *(omitted)* | Inventory list — every registered slot |
| `slot={id}` | Single creative (house fill if no paid booking) |

CORS: `GET`, `OPTIONS`; `Access-Control-Allow-Origin: *`.

## Slot ids (examples)

Wallet: `wallet.home.banner`, `wallet.swap.banner`, `wallet.send.banner`, `wallet.nft.banner`, …

Dev / other apps use their own `{app}.{surface}.{placement}` ids. Unknown slots return `400 UNKNOWN_SLOT` with the known list.

## Response (slot query)

```json
{
  "ok": true,
  "slot": "wallet.home.banner",
  "active": true,
  "source": "house",
  "creative": {
    "id": "ad-wallet.home.banner",
    "title": "…",
    "imageUrl": "https://…",
    "clickUrl": "https://…",
    "href": "https://…",
    "url": "https://…"
  }
}
```

House creatives are first-party suite promos (label **Riddle Suite** / **Featured**, never fake “Sponsored”). Paid auctions bid in **XRP** on Dev.

## Rules

- Do not block wallet unlock / sign on a slow ads fetch.
- Do not mix ads click URLs with SSO `return=` without allow-listing.
- Creatives must not promise financial returns. See [brand](/docs/brand).

---

## Xaman

slug: xaman

Xaman (Xumm) SignIn is a separate identity system from suite SSO. Do not mix session keys.

# Xaman

**Xaman SignIn is SEPARATE from suite SSO.**

| | Suite SSO | Xaman SignIn |
| --- | --- | --- |
| Home | [https://wallet.riddlewallet.com](https://wallet.riddlewallet.com) | Xaman app + Platform API on the **consuming app origin** |
| Cookie | `rdl_sess` `Domain=.riddlewallet.com` | Xaman payload UUID / app-local token |
| Message | `riddle-wallet:connected` | Xaman websocket / REST poll |
| Seed | Riddle on-device keystore | Keys in Xaman — never imported into Riddle |

## When to use which

- **Suite apps** (Swap, Bridge, Cafe, Hub, games, Dev) should connect **Riddle Wallet** first: [SSO](/docs/sso).
- **Xaman** remains valid for XRPL users who sign **payloads** (Payment, OfferCreate, SignIn) without using the Riddle keystore.
- Cafe broker offers, some Bridge deposits, and legacy XRPL flows still create Xaman payloads **on the app origin**.

## SignIn flow (Xaman only)

1. App origin creates a SignIn payload (Platform API; keys stay **server-side**).
2. User scans / opens Xaman.
3. App **polls** until signed or expired.
4. Store that session under an app-local key — **never** write it to `rdl_sess`.

## Do not

- Mix Xaman session keys with `rdl_sess`
- Ask a user who already has Riddle SSO to import a seed into Xaman (or vice versa) as a requirement
- Put Xumm API secrets in the browser

Suite connect details: [SSO](/docs/sso). Payload warnings still apply to Xaman-signed txs: [payloads](/docs/payloads).

---

## Brand

slug: brand

Solid colors only — no gradients. Sticky 44px header, solid footer, Inter, suite tokens.

# Brand

New suite chrome is **solid**. Do not add CSS gradients on header, footer, pills, or primary buttons.

## Color (locked)

| Token | Value | Use |
| --- | --- | --- |
| Background | `#07070c` | Page |
| Header / footer | `#09090b` | Sticky chrome |
| Card | `#111118` | Panels |
| Border | `#24262c` / `#1f1f2e` | Hairlines |
| Text | `#f4f4f8` | Body |
| Muted | `#8b8ba3` | Secondary |
| Accent | `#8b5cf6` | Links / focus — **solid**, not a fade |

Font: **Inter**. Theme color meta: `#07070c`. Logo: `/logo.jpg` or `/rdllogo.jpg` (same file family on every app).

**Forbidden** in product UI: `bg-gradient-*`, `linear-gradient(`, `radial-gradient(`.

## Header rules

- Sticky, **44px** row, solid `#09090b`, bottom border `#24262c`
- Logo + product label + horizontal **suite pills** (scroll, no wrap explosion)
- Account control: **Connect Wallet** when signed out (not “Connect Xaman” as the primary chrome label)
- Session + credits chip when connected
- No partner / upstream API hostnames in the header
- Safe-area: `padding-top: env(safe-area-inset-top)`

## Footer / bottom nav rules

- Mobile **bottom nav**: five suite destinations (Wallet · Cafe · Swap · Scan · Hub/More), solid `#09090b`, top border, touch targets ≥ 44px, safe-area bottom
- Desktop footer: suite app pills + **all** socials (X, Discord, Telegram, GitHub) + **one** promo + legal
- Product string: `Riddle {App} · {one-liner} · Not financial advice`
- Do not put fee disclaimers as gradient banners

## Copy

- Not financial advice
- No guaranteed returns in ad creatives or token-kit marketing
- Product names: Riddle Wallet, Riddle Swap, Riddle Bridge, … (see [suite map](/docs/suite-map))

---

## Adding an app

slug: adding-an-app

Checklist: ecosystem.ts, HUB_LINKS, hub catalog APPS, pills, live screenshots.

# Adding an app

A new production app is not live on the hub until **all** of these exist. Hosts must be real `*.riddlewallet.com` URLs.

## Checklist

1. **Wallet `lib/ecosystem.ts`** — add the production URL to `ECOSYSTEM` (env override + default host).
2. **`HUB_LINKS`** — append a card (title, description, `href`, `key`) so Wallet home / onboarding can launch it.
3. **Hub catalog `APPS` + `URLS`** — in this repo `src/lib/catalog.ts` (hub SSOT). `slug`, `name`, `href` from `URLS`, `group`, bullets, `shots`. **Do not invent hosts.** This docs portal reads `URLS` / `APPS` from that file.
4. **Pills** — if it belongs in the compact header switcher (~10 max), add it to `SUITE_APP_PILLS` in `src/lib/suite-shell-config.ts` (and the shared suite-chrome copy). Bottom nav stays the five primary destinations unless product asks otherwise.
5. **Screenshots** — capture **live production** only:

```
npm run capture
# node scripts/capture-live-apps.mjs
```

Writes `public/media/apps/{id}-desktop.png` and `{id}-mobile.png`. Point catalog `shots()` at those files. No generated art.

6. **SSO** — connect with `https://wallet.riddlewallet.com?return=&app={id}&action=connect`. Register `app` id on the Wallet allow-list. **No second seed import.** Cookie `rdl_sess` Domain=`.riddlewallet.com`.
7. **Ads / brand / payloads** — optional slot ids on `GET https://dev.riddlewallet.com/api/ads/active?slot=`. Solid chrome ([brand](/docs/brand)). Native units ([payloads](/docs/payloads)). Keep `/{slug}` redirect to the live host.

## After merge

- Hub `/apps/{slug}` and sitemap `/apps/*` pick up the catalog row automatically.
- Run capture again whenever the live UI changes.

---

## Guide bot

slug: guide-bot

POST /api/ai/guide on the hub. Server-only XAI_API_KEY. Model grok-4.6 via GUIDE_MODEL. 503 if the key is missing.

# Guide bot

The hub Guide answers product questions. It **never** receives seeds and must refuse import/export of keys.

## Endpoint

```
POST /api/ai/guide
```

Origin: [https://riddlewallet.com](https://riddlewallet.com) (same-origin from `/guide` and the in-app widget). Streams from `https://api.x.ai/v1/chat/completions`.

```json
{ "messages": [{ "role": "user", "content": "How do suite credits work?" }] }
```

No seeds, PINs, or private keys in the payload. `messages` is required.

Health: `GET /api/ai/guide/health` → `{ ok, configured, model }`.

## Server env

| Variable | Where | Role |
| --- | --- | --- |
| `XAI_API_KEY` | **Server only** | xAI key. Never `NEXT_PUBLIC_*`. Never ship to the browser. |
| `GUIDE_MODEL` | Server | Model id. Default **`grok-4.6`**. |
| `GUIDE_DISABLED` | Server | If `1`, route returns **503**. |

If `XAI_API_KEY` is missing, the route returns **HTTP 503** (`XAI_API_KEY is not configured`). Do not fall back to a browser key.

## Behavior

- Model: **grok-4.6** unless `GUIDE_MODEL` overrides
- Success is **SSE** (`text/event-stream`) from xAI
- Daily IP cap (20) → **429**
- Must refuse seeds, PINs, exploits, and return promises
- Cites `/apps`, `/features`, `/docs`, `/start`
- Fees / credits facts must match [fees & credits](/docs/fees-credits)

## Client

The Guide widget is owned by the hub shell. Feature pages should not fork a second bot. Docs for humans: [https://riddlewallet.com/guide](https://riddlewallet.com/guide).

---

## OpenAPI

slug: openapi

Public hub + ads + guide contracts: health, ads/active, POST /api/ai/guide, SSO query params.

# OpenAPI

Machine-readable copy of these pages: [docs.md](/docs.md) · [llms.txt](/llms.txt).

Ads live on **Dev** (`ADS_BASE` = https://dev.riddlewallet.com). Guide and health live on the **hub**.

## Hub HTTP routes

| Method | Path | Notes |
| --- | --- | --- |
| GET | /api/health | liveness |
| GET | /api/xrp | XRP USD quote |
| GET | /api/status | suite origin probes |
| GET | /api/ai/guide/health | `{ configured, model }` |
| POST | /api/ai/guide | Grok stream |
| GET | /go/bridge/:slug | SEO landing |
| GET | /go/swap/:slug | SEO landing |
| GET | /llms.txt | AI index |
| GET | /docs.md | all docs as markdown |
| GET | /sitemap.xml | sitemap |

```yaml
openapi: 3.0.3
info:
  title: Riddle Hub public API
  version: "1.0.0"
  description: Health, suite ads, and hub Guide. SSO is a Wallet query + cookie, not a JSON login.
servers:
  - url: https://riddlewallet.com
    description: Hub
  - url: https://dev.riddlewallet.com
    description: Dev (ads)
paths:
  /api/health:
    get:
      servers: [{ url: https://riddlewallet.com }]
      summary: Hub liveness
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok: { type: boolean, example: true }
                  service: { type: string, example: riddleweb }
                  at: { type: string, format: date-time }
  /api/ads/active:
    get:
      servers: [{ url: https://dev.riddlewallet.com }]
      summary: Live ad creative
      parameters:
        - in: query
          name: slot
          required: false
          schema: { type: string }
          description: Slot id. Omit to list inventory.
      responses:
        "200":
          description: Creative or inventory
        "400":
          description: UNKNOWN_SLOT
  /api/ai/guide:
    post:
      servers: [{ url: https://riddlewallet.com }]
      summary: Hub Guide (Grok)
      description: |
        Server-only XAI_API_KEY. Model grok-4.6 (GUIDE_MODEL).
        503 when the key is missing. Never send seeds. Streams SSE.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [messages]
              properties:
                messages:
                  type: array
                  items:
                    type: object
                    properties:
                      role: { type: string, enum: [user, assistant] }
                      content: { type: string }
      responses:
        "200":
          description: SSE stream (text/event-stream)
        "400":
          description: messages required / invalid JSON
        "429":
          description: Daily IP rate limit
        "503":
          description: XAI_API_KEY missing or GUIDE_DISABLED=1
```

## SSO (not REST)

`GET https://wallet.riddlewallet.com?return=&app=&action=connect`

Then cookie `rdl_sess` `Domain=.riddlewallet.com` and optional `postMessage` `riddle-wallet:connected`. See [SSO](/docs/sso).

## Units reminder

Payload amounts: **drops / lamports / wei** integers. Never `float * 1e6`.