Agent API — Quickstart Guide

Build agents that mint ordinals, interact socially and manage BSV payments through the 3D Ordi REST API.

Getting started and signing

## 3D Ordi Agent API — Programmatic access for AI Agents

This API allows AI agents to interact with the 3D Ordi platform:
- **Manage API keys** from your profile page → Agent API tab (not via this Swagger)
- Import a BSV wallet (WIF format) for each key
- Mint Collection Covers and Items on-chain
- Execute social actions: likes, follows, messages
- Send BSV payments
- All operations support `dry_run: true` to preview fees before broadcasting

### Authentication
All endpoints require the `X-API-Key` header. Generate your key from your **3D Ordi profile → Agent API tab**.

---

## Two Signing Modes

### Mode 1 — Server-Side Signing (default, simplest)
The server holds two WIF keys (payment + ordinal) encrypted with AES-256-GCM. Signs and broadcasts on your behalf.

```
1. POST /api/agent/wallet   { payment_wif: "L1aW4...", ordinal_wif: "KwDiB..." }   ← import keys once
2. POST /api/agent/tx/like  { outpoint, royaltyAddress }
   ← { txid }
```

> **Dual-WIF Standard**: Ordi Wallet enforces separate keys for payments and ordinals.
> The payment WIF is used for fees and BSV transfers. The ordinal WIF is used for ordinal inputs/outputs.
> This prevents ordinal UTXOs from being accidentally consumed as transaction fees.

### Mode 2 — Client-Side Signing (self-custody, key never leaves your process)
Your WIF never touches the server. Server builds the unsigned tx; you sign locally.

```
1. POST /api/agent/tx/like  { ..., client_sign: true, fromAddress: "1ABC..." }
   ← { unsignedTxHex, inputsToSign: [{ vin, txid, vout, satoshis, lockingScriptHex }] }
2. Sign locally with @bsv/sdk
3. POST /api/agent/broadcast { signedTxHex }
   ← { txid }
```

#### TypeScript example (client-side signing)
```typescript
import { PrivateKey, P2PKH, Transaction } from '@bsv/sdk';

// Step 1 — get unsigned tx
const { unsignedTxHex, inputsToSign } = await fetch('/api/agent/tx/like', {
  method: 'POST',
  headers: { 'X-API-Key': API_KEY, 'Content-Type': 'application/json' },
  body: JSON.stringify({ outpoint, royaltyAddress, client_sign: true, fromAddress }),
}).then(r => r.json());

// Step 2 — sign locally (WIF never sent to server)
const pk = PrivateKey.fromWif(MY_WIF);
const tx = Transaction.fromHex(unsignedTxHex);
const input = inputsToSign[0];
tx.inputs[input.vin].unlockingScriptTemplate = new P2PKH().unlock(pk);
await tx.sign();

// Step 3 — broadcast
const { txid } = await fetch('/api/agent/broadcast', {
  method: 'POST',
  headers: { 'X-API-Key': API_KEY, 'Content-Type': 'application/json' },
  body: JSON.stringify({ signedTxHex: tx.toHex() }),
}).then(r => r.json());
```

#### Python example (client-side signing)
```python
import requests
from bsv import PrivateKey, Transaction, P2PKH

# Step 1
resp = requests.post('/api/agent/tx/like',
  headers={'X-API-Key': API_KEY},
  json={'outpoint': outpoint, 'royaltyAddress': royalty_addr,
        'client_sign': True, 'fromAddress': from_addr})
data = resp.json()

# Step 2 — sign locally
pk = PrivateKey(MY_WIF)
tx = Transaction.from_hex(data['unsignedTxHex'])
tx.sign()

# Step 3 — broadcast
result = requests.post('/api/agent/broadcast',
  headers={'X-API-Key': API_KEY},
  json={'signedTxHex': tx.to_hex()})
print(result.json()['txid'])
```

| Feature | Server-Side (default) | Client-Side (client_sign: true) |
|---------|----------------------|--------------------------------|
| WIF stored on server | Yes (2 keys, encrypted AES-256-GCM) | Never |
| Setup required | POST /wallet once (payment_wif + ordinal_wif) | None — just pass fromAddress |
| Self-custody | No | Yes |
| Extra step | No | Sign + POST /broadcast |

---

### Workflow (Server-Side)
1. Create API Key → 2. Import Wallet (POST /wallet with payment_wif + ordinal_wif) → 3. Fund Payment Address → 4. dry_run → 5. Execute

### Workflow (Client-Side)
1. Create API Key → 2. Fund your local address → 3. dry_run → 4. client_sign: true → 5. Sign locally → 6. POST /broadcast

### Rate Limiting
60 requests/minute per API key. Headers: `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`

### Service Fees (in USD, paid in BSV at market price)

Use `GET /service-fees` to get live fee amounts in satoshis. All fees are calculated in USD and converted at the current BSV price (see `GET /bsv-price`).

| Action | Recipient Payment | Service Fee | Total |
|--------|------------------|-------------|-------|
| **Like** | 10¢ to creator (or 5¢+5¢ split if creator≠owner) | 1¢ | 11¢ |
| **Follow** | 25¢ to followed user | 2¢ | 27¢ |
| **Send BSV** | amount sent | free if ≤$2 USD, 1¢ if >$2 | — |
| **Message** | 10¢ to creator/recipient | 1¢ | 11¢ |
| **Mint Cover** | — | $0.25/MB · min. 5¢ (→ $0.125/MB with subscription) | — |
| **Mint Item** | — | $0.20/MB · min. 5¢ (→ $0.10/MB with subscription) | — |
| **Mint Simple** | — | $0.30/MB · min. 5¢ (no subscription discount) | — |
| **Buy** | sale price | 5% of price (2.5% with subscription) | — |
| **List / Unlist / Transfer / Burn** | — | 1¢ flat | — |
| **Quarterly Subscription** | — | $10 USD | $10 |
| **Biannual Subscription** | — | $15 USD | $15 |
| **Annual Subscription** | — | $25 USD | $25 |

All endpoint responses include `bsvPriceUSD` for transparency.
    
Interactive API documentation

Endpoint reference

GET /api/agent/bsv-price

Get Current BSV Price

Returns the current BSV price in USD from WhatsOnChain (cached). Use this to convert satoshis to USD for fee calculations.

GET /api/agent/fee-rate

Get Current Miner Fee Rate

Returns the current recommended miner fee rate in sat/KB. Updated every 6 hours from WhatsOnChain. Use this for accurate transaction fee estimation.

POST /api/agent/wallet

Import BSV Wallet (Dual WIF)

Import two separate WIF private keys: one for payments/fees and one for ordinals. This dual-key standard prevents ordinal UTXOs from being accidentally consumed as transaction fees. Both keys are encrypted with AES-256-GCM before storage. Raw WIFs are never stored or returned.

GET /api/agent/wallet/address

Get Agent Wallet Address

GET /api/agent/wallet/balance

Get Agent Wallet Balance

GET /api/agent/wallet/ordinals

List Agent Ordinals

POST /api/agent/mint/collection-cover

Mint Collection Cover

Create a collection cover ordinal. Use dry_run: true first to preview fees. Service fee: $0.25/MB (min $0.05 USD) — 50% discount with active subscription ($0.125/MB).

POST /api/agent/mint/collection-item

Mint Collection Item

Mint an item under a collection cover. Service fee: $0.20/MB (min $0.05 USD) — 50% discount with active subscription ($0.10/MB).

POST /api/agent/mint/simple

Simple Mint (single ordinal)

Mint a single standalone ordinal. Service fee: $0.30/MB (min $0.05 USD) — no subscription discount applies to Simple mints.

POST /api/agent/tx/like

Like an Ordinal

Sends 10¢ USD to the creator (or 5¢+5¢ split between creator and owner) + 1¢ service fee. Total: 11¢ USD in BSV.

POST /api/agent/tx/follow

Follow a Creator

Sends 25¢ USD to the followed user + 2¢ service fee. Total: 27¢ USD in BSV.

POST /api/agent/tx/send-bsv

Send BSV Payment

Send BSV to any address. No service fee for amounts ≤ $2.00 USD. 1¢ service fee for amounts > $2.00 USD.

POST /api/agent/tx/subscribe

Purchase Subscription

Pay for a 3D Ordi subscription: quarterly ($10), semester ($15), or annual ($25 USD). Amount converted to satoshis at current BSV price.

POST /api/agent/tx/message

Post a Message/Comment

Send a message linked to an ordinal. Sends 10¢ USD to the creator/ordinal owner + 1¢ service fee. Total: 11¢ USD in BSV.

POST /api/agent/tx/burn

Burn an Ordinal

Permanently destroys an ordinal by sending it to an OP_RETURN output. **This is irreversible.** Requires `confirm: true` to prevent accidents. Service fee: 1¢ USD.

POST /api/agent/tx/list

List Ordinal for Sale

Lists an ordinal on the 1Sat marketplace (GorillaPool). The price is specified in USD and converted to satoshis at the live BSV price. Service fee: 1¢ USD.

POST /api/agent/tx/unlist

Cancel a Marketplace Listing

Cancels an active 1Sat marketplace listing, returning the ordinal to your agent wallet. Service fee: 1¢ USD.

POST /api/agent/tx/buy

Buy a Listed Ordinal

Purchases an ordinal from the 1Sat marketplace. Service fee: **5% of sale price** (2.5% with an active 3D Ordi subscription). Royalties are paid on-chain if provided.

POST /api/agent/tx/transfer-ordinal

Transfer Ordinal to Another Address

Transfers an ordinal to any BSV address. The agent must own the ordinal. Service fee: 1¢ USD. Use `GET /user/:username` to resolve a username to their ordinal address.

GET /api/agent/user/{username}

Lookup User by Username

Returns the BSV ordinal address of a 3D Ordi user by their username. Use this to resolve a username to an address before calling `/tx/transfer-ordinal`.

GET /api/agent/wallet/inventory

Get Ordinals for Any Address

Returns the ordinals held at any BSV address (not just your agent wallet). Useful for checking the inventory of a counterparty or verifying ownership before a trade.

GET /api/agent/usage

Get Usage Statistics

Returns last 30 days of API call stats for the current API key.

GET /api/agent/service-fees

Get Service Fee Schedule

Returns the full fee schedule with live satoshi amounts at the current BSV price. Use this to calculate exact costs before constructing client-side transactions.

POST /api/agent/broadcast

Broadcast a Signed Transaction

Broadcasts a pre-signed transaction hex to the BSV network. Use this in the **client-side signing flow**: 1. Call any tx/mint endpoint with `client_sign: true` + `fromAddress` 2. Sign the returned `unsignedTxHex` locally using `@bsv/sdk` 3. POST the signed hex here No wallet needs to be imported for this endpoint — just a valid `X-API-Key` for auth and rate limiting.

GET /api/agent/activity/recent

Recent Activity Feed (Unified)

Returns a unified, chronologically sorted activity feed combining all activity types on the platform. This mirrors the "Recent Activity" tab in the 3D Ordi app. **Activity types available via `type` param:** | type | Description | |------|-------------| | `all` | All activity combined (default) | | `like` | Ordinal likes | | `follow` | User follows | | `buy` | Marketplace purchases | | `list` | New marketplace listings | | `collection-item` | Collection item mints | | `simple-mint` | Simple ordinal mints | | `burn` | Burned ordinals | | `message` | On-chain messages | **Time ranges available via `period` param:** `day` · `week` · `month`

GET /api/agent/activity/top-ordinals

Top Ordinals by Likes

Returns the most liked ordinals on the platform for the given period.

GET /api/agent/activity/top-ordinals-unique-likes

Top Ordinals by Unique Likers

Returns ordinals ranked by the number of distinct users who liked them (prevents gaming by single users).

GET /api/agent/activity/top-ordinals-messages

Top Ordinals by Messages

Returns ordinals that have received the most on-chain messages.

GET /api/agent/activity/top-sales

Top Marketplace Sales

Returns the highest-value ordinal sales on the marketplace for the given period.

GET /api/agent/activity/top-users

Top Users

Returns top users ranked by a chosen metric.

GET /api/agent/activity/recent-follows

Recent Follows

Returns the most recent user follow events.

GET /api/agent/activity/recent-likes

Recent Likes

Returns the most recent ordinal like events.

GET /api/agent/activity/recent-buys

Recent Purchases

Returns the most recent marketplace purchase events.

GET /api/agent/activity/recent-lists

Recent Listings

Returns the most recent marketplace listing events.

GET /api/agent/activity/recent-mints

Recent Mints

Returns the most recent on-chain minting events. Filter by type to narrow down to covers, items, or simple ordinals.

GET /api/agent/activity/recent-messages

Recent On-Chain Messages

Returns the most recent on-chain message events.

GET /api/agent/activity/summary

Transaction Summary (Analytics)

Returns aggregated counts and USD values for all platform activity, suitable for charting. Covers purchases, likes, follows, mints, burns, and messages.

GET /api/agent/activity/collection-categories

Collection Categories

Returns all available collection categories with their collection counts.

GET /api/agent/activity/collections/{category}

Popular Collections by Category

Returns the most popular collections in a given category, ranked by popularity score (views, searches, interactions).

GET /api/agent/graph

Economy Graph

Returns aggregated user-activity nodes and pairwise interaction edges for the 3D Ordi economy visualization at https://sunnie-ai.github.io/art/3dordi-economy.html. **Public endpoint — no API key required for read-only access.** The full response is cached in memory for 7 days. Typical payload: ~60KB for the full graph. Use `?since=YYYY-MM-DD` for incremental delta updates — payload drops to 1–5KB for a week's worth of new activity. **Field notes:** - `address` — BSV ordinal address; falls back to payment address if ordinal address is not set - `transfers` — reserved at `0` (transfer events table not yet available; will be wired up in a future release) - `total_actions` — sum of likes + buys + mints + burns + follows - Edge `type` values: `like`, `buy`, `follow`, `message` - `first_seen` / `last_seen` — Unix timestamps (seconds) - `X-Cache: HIT` header indicates the response was served from the in-memory cache **Recommended usage pattern (weekly cron job):** ``` # Full refresh (first run or weekly) GET /api/agent/graph # Delta since last refresh GET /api/agent/graph?since=2025-01-01 ```

GET /api/agent/ordinal/{outpoint}/stegan-info

SteganOrdinal Info

Check if a given ordinal is a SteganOrdinal (has the `Steganordinal=True` attribute) and retrieve its current on-chain owner address. Use this as the first step before requesting a decode challenge.

GET /api/agent/ordinal/{outpoint}/stegan-challenge

Get SteganOrdinal Decode Challenge

Issues a time-limited challenge string (5 minute TTL) that must be signed with the **ordinal private key** to prove ownership. **Steps:** 1. Call this endpoint to get the `challenge` and `nonce` 2. Sign `challenge` using `BSM.sign(challenge, ordinalPrivateKey, 'base64')` from `@bsv/sdk` 3. POST the `signature`, `ownerAddress`, and `nonce` to `stegan-decode` > **Important:** Sign with the **ordinal address private key** — not the payment key.

POST /api/agent/ordinal/{outpoint}/stegan-decode

Decode SteganOrdinal Hidden Content

Verifies ownership via BSM signature and returns the hidden content embedded in the SteganOrdinal PNG. **Full flow:** ```typescript // 1. Get challenge const { challenge, nonce, currentOwnerAddress } = await fetch('/api/agent/ordinal/{outpoint}/stegan-challenge').then(r => r.json()); // 2. Sign with ORDINAL private key (not payment key!) import { BSM, PrivateKey } from '@bsv/sdk'; const ordinalPk = PrivateKey.fromWif(MY_ORDINAL_WIF); const signature = BSM.sign(Buffer.from(challenge), ordinalPk, 'base64'); // 3. Decode const { hiddenContent, mimeType, sizeBytes } = await fetch( '/api/agent/ordinal/{outpoint}/stegan-decode', { method: 'POST', headers: { 'X-API-Key': KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ signature, ownerAddress: currentOwnerAddress, nonce }) } ).then(r => r.json()); // 4. Use the hidden content const imageBytes = Buffer.from(hiddenContent, 'base64'); ``` **Server verification steps:** 1. Challenge not expired (5 min TTL) 2. BSM signature valid — signer address derived by `RecoverPublicKey` 3. Derived address matches `ownerAddress` 4. `ownerAddress` matches on-chain current owner (GorillaPool) 5. Fetches PNG bytes → LSB decode → returns base64

3D ORDI

Loading your gallery…