# StableDomains — Agent Skill

Register a domain on [stabledomains.dev](https://stabledomains.dev) end-to-end using **AgentCash**. No API keys. Identity is your wallet; paid routes settle in USDC via x402/MPP.

Use this when a user asks you to buy, register, check, renew, or manage DNS for a domain through StableDomains.

## 1. Install AgentCash (if needed)

AgentCash is invoked via `npx` — nothing to install globally. First run creates a wallet at `~/.agentcash/wallet.json`.

```bash
# Check whether a wallet already exists and see balance
npx agentcash@latest balance

# First-time setup (wallet + MCP). Pass an invite code if you have one.
npx agentcash@latest onboard -y
# or: npx agentcash@latest onboard -y --invite YOUR_CODE
```

If balance is too low for the purchase:

```bash
npx agentcash@latest accounts          # deposit links per network
npx agentcash@latest redeem <code>     # optional invite credit
```

**Typical costs:** search $0.01, check ~$0.01–0.05, registration $20–150 depending on TLD and daily bonding curve.

Optional but recommended — persist StableDomains guidance in the agent client:

```bash
npx agentcash@latest add https://stabledomains.dev
```

For local dev against a running app:

```bash
npx agentcash@latest add http://localhost:3004 --dev
```

## 2. Discover the API

Always read live guidance before guessing field names. **Before the first POST fetch**, probe the endpoint schema:

```bash
npx agentcash@latest discover https://stabledomains.dev --include-guidance --format pretty
npx agentcash@latest check https://stabledomains.dev/api/search -m POST --format pretty
npx agentcash@latest check https://stabledomains.dev/api/check -m POST --format pretty
npx agentcash@latest check https://stabledomains.dev/api/profile -m POST --format pretty
npx agentcash@latest check https://stabledomains.dev/api/profile/verify-email -m POST --format pretty
npx agentcash@latest check https://stabledomains.dev/api/register -m POST --format pretty
```

Profile uses broken-down ICANN fields — **not** `name` or `address`. Verify-email uses **`code`**, not `otp`.

Set a base URL for the rest of this guide:

```bash
ORIGIN="${ORIGIN:-https://stabledomains.dev}"
```

Get the paying wallet address (registration charges Base by default):

```bash
WALLET="$(npx agentcash@latest accounts --format json | jq -r '.accounts[] | select(.network=="base") | .address' | tr '[:upper:]' '[:lower:]')"
echo "Paying wallet: $WALLET"
```

## 3. End-to-end: buy a domain

Order: **search (optional) → check → profile → register**. Check availability and price before purchase; collect ICANN registrant contact only after the user confirms. Profile routes use **SIWX** (wallet signature). Search, check, register, and renew use **x402** (USDC payment). `agentcash fetch` handles both.

**Never ask the user for a wallet address.** The paying wallet is established automatically by x402/MPP on paid routes.

### Step 0 — Search across TLDs (optional, x402, $0.01)

Use when the user has a **name but no TLD** (e.g. "coolstartup", not yet "coolstartup.com"):

```bash
NAME="coolstartup"

npx agentcash@latest fetch "$ORIGIN/api/search" -m POST \
  -b "$(jq -nc --arg n "$NAME" '{name:$n}')" \
  -p x402 --payment-network base \
  --format pretty
```

- Body: `{ "name": "coolstartup" }` — or `{ "name": "coolstartup.com" }` (only the label before the first dot is used).
- Response includes `availableDomains[]` — **available TLDs only**, no registration prices.
- Present results as **one domain per markdown bullet line** (never comma-join domains).
- After the user picks a domain, continue to Step A for price and purchase confirmation.

Search also returns `readyToRegister` for the paying wallet when `availableCount > 0`.

### Step A — Check availability and price (x402, ~$0.01–0.05)

When the user names a full domain (or after Step 0):

```bash
DOMAIN="my-cool-project.com"

npx agentcash@latest fetch "$ORIGIN/api/check" -m POST \
  -b "$(jq -nc --arg d "$DOMAIN" '{domain:$d}')" \
  -p x402 --payment-network base \
  --format pretty
```

If `available` is `false`, stop — pick another name with the user.

Quote only `currentPrice` as the registration price. Do not mention `basePrice`, `bondingMultiplier`, or `dailySlotsRemaining` to the user — those are internal.

**STOP after quoting.** Get **explicit confirmation** to purchase before the next step.

Check `readyToRegister` in the search/check response:

- If `true`: the paying wallet already has a verified profile (`profile.email` shows the registered email). **Do not collect ICANN contact info.** Skip to Step D after confirmation.
- If `false` or absent: collect profile (Steps B–C) after confirmation.

### Step B — Save profile (SIWX, free)

**Skip this step when `readyToRegister` is true from Step A.**

Only after the user confirms purchase at the quoted price, and only when profile is not already verified. Map user input to API fields:

| User says                   | API field                                   |
| --------------------------- | ------------------------------------------- |
| first name                  | `firstName`                                 |
| last name                   | `lastName`                                  |
| email                       | `email`                                     |
| street address              | `addressLine1`                              |
| apt / suite (optional)      | `addressLine2`                              |
| city                        | `city`                                      |
| state / province (optional) | `state`                                     |
| postal / ZIP code           | `postalCode`                                |
| country                     | `country` (ISO 3166-1 alpha-2, e.g. `"US"`) |

Do **not** send `{ name }`, `{ address }`, or `{ otp }`.

```bash
npx agentcash@latest check "$ORIGIN/api/profile" -m POST --format pretty

npx agentcash@latest fetch "$ORIGIN/api/profile" -m POST -b "$(jq -nc \
  --arg fn "Ada" --arg ln "Lovelace" --arg em "ada@example.com" \
  '{firstName:$fn,lastName:$ln,email:$em,addressLine1:"123 Main St",city:"San Francisco",state:"CA",postalCode:"94105",country:"US"}')" \
  --format pretty
```

This sends a **6-digit OTP** to the email. Ask the user to read you the code (you cannot read their inbox unless they grant access). Response `nextStep` confirms the verify-email body shape.

### Step C — Verify email (SIWX, free)

**Skip this step when `readyToRegister` is true from Step A.**

**Field is `code`, not `otp`.** Must be exactly 6 digits.

```bash
npx agentcash@latest check "$ORIGIN/api/profile/verify-email" -m POST --format pretty

npx agentcash@latest fetch "$ORIGIN/api/profile/verify-email" -m POST \
  -b "$(jq -nc --arg c "482913" '{code:$c}')" \
  --format pretty
```

Replace `482913` with the user's OTP. Response `nextStep` directs you to register.

### Step D — Register the domain (x402, $20–150)

Body is **only** `{ domain }`. The paying wallet comes from x402 — **never ask the user for a wallet address.**

```bash
npx agentcash@latest fetch "$ORIGIN/api/register" -m POST \
  -b "$(jq -nc --arg d "$DOMAIN" '{domain:$d}')" \
  -p x402 --payment-network base \
  --format pretty
```

**Rules:**

- Do not ask the user for their wallet — AgentCash supplies it via payment.
- Profile must be email-verified (steps B–C, or `readyToRegister: true` from check) before register succeeds.

**Tell the user:**

- `domain`, `registrationEmailSent`, and `nextStep` from the response.
- A confirmation email is sent to the profile email when registration completes.

**Never share with the user:** nameservers, hosted zone IDs, order IDs, or payment transaction hashes.

### After registration — status and attestation

Poll until the domain is live:

```bash
npx agentcash@latest fetch "$ORIGIN/api/domain/status?domain=$DOMAIN" --format pretty
```

Poll until `status` is `"active"` and `nsPropagated` is `true` before configuring DNS.

Registration publishes a `_stabledomains.{domain}` TXT record:

```
stabledomains-owner=v1;wallet={ownerWallet}
```

Public ownership lookup (no auth):

```bash
npx agentcash@latest fetch "$ORIGIN/api/domain/verify?domain=$DOMAIN" --format pretty
```

Optional: add `&wallet=0x...` to check whether a wallet matches the attestation.

## 4. Manage existing domains

### List and profile

```bash
# Domains owned by the authenticated wallet
npx agentcash@latest fetch "$ORIGIN/api/domain/list" --format pretty

# Read profile readiness before another registration
npx agentcash@latest fetch "$ORIGIN/api/profile" --format pretty
```

Human owners can also view domains at [stabledomains.dev/dashboard](https://stabledomains.dev/dashboard) (email OTP login). That portal path is separate from agent SIWX/x402 registration.

### Renew (x402)

```bash
npx agentcash@latest fetch "$ORIGIN/api/domain/renew" -m POST \
  -b "$(jq -nc --arg d "$DOMAIN" '{domain:$d,count:1}')" \
  -p x402 --payment-network base \
  --format pretty
```

- `count` is 1–10 years (defaults to 1). Price = per-year renewal price × count (see TLD table).
- Must be the domain owner wallet (from x402 payment).

### DNS (SIWX)

Read records:

```bash
npx agentcash@latest fetch "$ORIGIN/api/domain/dns?domain=$DOMAIN" --format pretty
```

Modify records:

```bash
npx agentcash@latest fetch "$ORIGIN/api/domain/dns" -m POST \
  -b "$(jq -nc --arg d "$DOMAIN" '{domain:$d,action:"upsert",records:[{type:"A",name:"'$DOMAIN'",value:"1.2.3.4",ttl:300}]}')" \
  --format pretty
```

- Supported types: A, AAAA, CNAME, MX, TXT, SRV, CAA. NS records cannot be modified.
- The `_stabledomains` attestation TXT is owner-editable (upsert only, not deletable) and syncs `ownerWallet` when updated.
- MX values include priority (e.g. `"10 mail.example.com"`). `ttl` defaults to 300.

### Transfer-out (SIWX)

```bash
npx agentcash@latest fetch "$ORIGIN/api/domain/transfer-out" -m POST \
  -b "$(jq -nc --arg d "$DOMAIN" '{domain:$d}')" \
  --format pretty
```

60-day ICANN transfer lock applies after registration.

## 5. Quick reference

| Step             | Endpoint                         | Auth | Cost        |
| ---------------- | -------------------------------- | ---- | ----------- |
| Search TLDs      | `POST /api/search`               | x402 | $0.01       |
| Check domain     | `POST /api/check`                | x402 | ~$0.01–0.05 |
| Read profile     | `GET /api/profile`               | SIWX | free        |
| Save profile     | `POST /api/profile`              | SIWX | free        |
| Verify OTP       | `POST /api/profile/verify-email` | SIWX | free        |
| Register         | `POST /api/register`             | x402 | $20–150     |
| Domain status    | `GET /api/domain/status`         | SIWX | free        |
| Ownership lookup | `GET /api/domain/verify`         | none | free        |
| List domains     | `GET /api/domain/list`           | SIWX | free        |
| Read DNS         | `GET /api/domain/dns`            | SIWX | free        |
| Modify DNS       | `POST /api/domain/dns`           | SIWX | free        |
| Renew            | `POST /api/domain/renew`         | x402 | varies      |
| Transfer-out     | `POST /api/domain/transfer-out`  | SIWX | free        |

### TLD pricing (before bonding multiplier)

Supported TLDs (28): .com, .org, .net, .info, .biz, .co, .me, .mobi, .name, .tv, .uk, .cloud, .shop, .store, .tech, .online, .site, .pro, .email, .studio, .page, .link, .live, .xyz, .dev, .app, .io, .ai

| TLD                                                                                                        | Register | Renew | Check |
| ---------------------------------------------------------------------------------------------------------- | -------- | ----- | ----- |
| .com / .org / .net / .info / .biz / .co / .me / .mobi / .name / .uk                                        | $20      | $20   | $0.01 |
| .cloud / .shop / .store / .tech / .online / .site / .pro / .email / .studio / .page / .link / .live / .xyz | $20      | $20   | $0.05 |
| .tv                                                                                                        | $40      | $40   | $0.01 |
| .dev / .app                                                                                                | $25      | $25   | $0.05 |
| .io                                                                                                        | $85      | $85   | $0.05 |
| .ai                                                                                                        | $150     | $150  | $0.05 |
| Search (all TLDs)                                                                                          | —        | —     | $0.01 |

Daily bonding curve (internal): slots 1–5 at 1.0×, 6–7 at 1.5×, 8–9 at 2.5×, slot 10 at 5.0×. Quote only `currentPrice` to users — never base price or slots remaining.

## 6. Local dev

Use `STABLEDOMAINS_AUTH_TEST_MODE=1` and `pnpm dev:auth-test` to capture OTPs via `GET /api/dev/auth/otp?email=...`.

Profile + verify flow against local Postgres: `apps/stabledomains/scripts/test-e2e-agentcash.sh` (set `DOMAIN=` to exercise paid routes).

## 7. Troubleshooting

| Issue                             | What to do                                                                                                                   |
| --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `Insufficient balance`            | `npx agentcash@latest balance`, then `accounts` or `redeem`                                                                  |
| `No domains found for this email` | Portal login only — agent registration uses profile, not portal OTP                                                          |
| 400 on register                   | Profile not verified or domain unavailable                                                                                   |
| 403 on renew / DNS                | Wrong wallet — must be the domain owner wallet from SIWX/x402                                                                |
| 409 on DNS                        | Domain status is `expired` — renew before managing DNS                                                                       |
| Payment succeeded but DNS pending | Poll `GET /api/domain/status?domain=...` until `nsPropagated` is true                                                        |
| Wrong request body (400)          | Run `agentcash check <url> -m POST` first — profile needs firstName/addressLine1, not name/address; verify uses code not otp |

## 8. Agent checklist

- [ ] AgentCash wallet funded on Base
- [ ] User chose domain name (used `/api/search` when they had no TLD)
- [ ] Price quoted from `/api/check`; user confirmed purchase
- [ ] Schema checked with `agentcash check` before profile/verify POSTs
- [ ] Profile saved with user's real contact info (firstName, addressLine1, etc.)
- [ ] Email OTP verified (or `readyToRegister: true` from search/check)
- [ ] User given registration status and confirmation email status — not infra internals
- [ ] Polled domain status until active; mentioned DNS TXT attestation only at high level if relevant
