---
name: tipsforsips-publish
description: Publish content to Tips for Sips, an XMR-native creator platform. Every publish is an HTTP 402 — the agent pays a per-post fee (0.0005 XMR) into the platform wallet. Covers creator setup, agent-key minting, wallet funding, publish → 402 → pay → confirm, and error recovery. Use when a user wants to auto-publish posts (their own writing, analysis, market notes, agent output) and monetize through Tips for Sips.
---

# Tips for Sips — Agent Publish (402x)

Tips for Sips (TFS) is an XMR-native Patreon/BMAC competitor: creators post, fans tip/pay in Monero. This skill lets an **agent auto-publish on its human's behalf**. Every publish answers **HTTP 402 Payment Required**: the agent pays a flat **0.0005 XMR** (~$0.26) per post into the platform's fee wallet, then confirms with the tx proof. Creator content *earnings* (tips, gated unlocks) stay peer-to-peer — TFS never touches them.

Two roles exist on the platform:
- **Human creators** publish through the web app (no 402 — they post directly).
- **Agent lanes** publish through the machine API only, authenticated by an **agent key**, and every post pays the 402 fee.

This skill is the agent lane. It has three phases: **Setup** (human does once), **Fund** (human funds a wallet), **Publish** (agent does, forever after).

## Phase 0 — The human's wallet (REQUIRED before any publish)

The agent cannot pay without Monero. The human needs a funded XMR wallet. Exact guidance to give the human:

> **You need a Monero wallet with a little XMR in it to pay per-post fees (~0.0005 XMR ≈ $0.26 per post).**
> - Recommended: **Cake Wallet** or **Unstoppable Wallet** — both are multi-coin with **Lightning integration** for easy funding.
> - **Cake flow (agent-assisted):** the agent creates a Monero CLI wallet from **getmonero.org** (official CLI: `monero-wallet-cli`), notes the **25-word seed phrase** (Monero's standard: 24 words + 1 checksum word — Cake will ask for 25), and the human imports that phrase into Cake Wallet (Cake → "Import wallet" → Monero → 25 words). Cake can then fund the wallet (buy / receive), while the agent uses the Monero CLI wallet to sign and send the per-post fees.
> - **Custody boundary (both lanes):** the agent helps create the CLI wallet but **never sees, stores, or transmits the seed phrase** — it appears only on the human's screen in the CLI wallet. The human does the phrase→Cake import themselves. Before funding, the human verifies the Cake receive address **matches** the CLI wallet's address (run `address` in the CLI, compare to Cake's receive screen) — if they differ, stop: never fund.
> - Unstoppable works the same way (multi-coin + Lightning) if the human prefers it.
> - **Need Monero? Check out XMRHub.org for ways to buy, swap, and earn Monero.** *(XMRHub is a referral partner — TFS earns a small commission on purchases made through it.)*

The agent may help create the CLI wallet but must never hold the seed alone — the human owns it. Keep the seed phrase in the human's custody (password manager). The CLI wallet's **spend key / tx key stays with the human's wallet**; TFS never sees it (platform verification is spend-key-free by design).

## Phase 1 — Creator setup (human, once)

The platform URL and endpoints are environment-configured: `TFS_BASE` (default `https://tipsforsips.com/api/v1`). All requests are JSON; the API is Fastify.

1. **Register** (or have the human sign up at the web app): `POST {TFS_BASE}/auth/register` with `{"email": "...", "password": "...", "displayName": "..."}`. Password: ≥8 chars, upper+lower+digit. Response sets a session cookie — keep it (cookie jar) for the next call. If `EMAIL_EXISTS` (400), the account already exists — have the human log in via the web app instead.
2. **Create the creator profile**: `POST {TFS_BASE}/creators` with `{"username": "..."}` (3–30 chars, `[a-z0-9_]`). 201 = profile ready. Errors: `CREATOR_EXISTS` (409, already done), `USERNAME_TAKEN` (409, pick another).
   - If the human already has an account/profile from the web app, skip both steps — the key mint below only needs a logged-in creator session.

## Phase 2 — Mint an agent key (human authorizes, once per agent)

The agent key is what lets the *machine* act as the creator. Minted once, shown once.

1. `POST {TFS_BASE}/agent/keys` with the session cookie from Phase 1, body `{"name": "my-agent-name"}` (optional, ≤60 chars).
2. Response 201: `data.key` = a raw key like `tfsk_` + 64 hex chars. **The server stores only a hash — this is the ONLY time the raw key is returned.**
3. **Store the raw key securely NOW** — env var `TFS_AGENT_KEY`, a secrets file, or the platform's keychain. If it's lost, revoke and re-mint: `DELETE {TFS_BASE}/agent/keys/{id}` (session cookie).
4. Rate/safety bounds: max 5 active keys per creator; revoked keys 401 immediately.

## Phase 3 — Publish (agent, every post)

Authenticate with the key: header `Authorization: Bearer tfsk_…` or `x-agent-key: tfsk_…`.

### 3a. Demand
```
POST {TFS_BASE}/agent/post
Authorization: Bearer tfsk_...
{
  "title": "Post title (≤500 chars)",
  "contentHtml": "<p>Post body — simple HTML; paragraphs as <p>…</p>.</p>",
  "excerpt": "Optional teaser (≤500 chars)"
}
```
- `contentHtml` is **sanitized server-side** (allowlist: p, br, strong, em, a[href http/https/mailto], lists, quotes, code, h2–h5). Don't send scripts, iframes, inline event handlers, or `javascript:` URLs — they are stripped. Plain text with blank lines is safest.
- Drafts are public-only (agent v1). Gated/subscriber posts are not available on the machine lane yet.

**Response 402** (this is the product, not an error):
```json
{ "success": false,
  "error": { "code": "PAYMENT_REQUIRED", "message": "…" },
  "payment": {
    "paymentId": "…", "amountXmr": "0.0005",
    "amountFiat": 0.26, "address": "…8-prefix subaddress…",
    "moneroUri": "monero:ADDR?tx_amount=0.0005",
    "expiresAt": "<15 min>", "confirmUrl": "/api/v1/agent/post/confirm"
  }
}
```
The fee amount is **exact** (0.0005 XMR). Pay that amount to `payment.address` before `expiresAt` (15 min). If it expires, re-demand (new 402) — unpaid demands just age out.
**Refund policy (read before paying):** fees are **non-refundable** by ToS once a payment is made — if your draft is later rejected (e.g. the draft schema changed between demand and confirm), the fee stands; re-demand with a fresh draft. Only platform-fault cases (creator deleted mid-payment, late tx after grace) qualify for a manual cold-key refund — contact support with the paymentId. Paying the fee = agreeing to this.

### 3b. Pay from the human's wallet
The agent drives the Monero CLI wallet (the human funded it in Phase 0):
```
monero-wallet-cli --wallet-file <path> --daemon-address <node>
  pay <payment.address> 0.0005
```
CLI prints the **txid** and **tx key** (`tx_key`). Keep both — the tx key is the proof. (The CLI may need `set tx-key` … follow its prompts to reveal `tx_key`.) If the human prefers, they can send from Cake/Unstoppable and export the tx key there — same fields needed below.

### 3c. Confirm
```
POST {TFS_BASE}/agent/post/confirm
Authorization: Bearer tfsk_...
{ "paymentId": "<from 402>", "txid": "<64-hex>", "txKey": "<64-hex>" }
```
- 201 = published. Response includes the post (id, title, slug, url).
- Verification is stateless and spend-key-free (xmr-pay against Monero nodes) — the platform never touches the wallet's spend key.
- **Non-refundable policy**: if the confirm is rejected because the stored draft no longer validates (`409 DRAFT_NO_LONGER_VALID` — e.g., a schema change between demand and confirm) the fee is non-refundable by ToS; the agent should re-demand with a fresh draft. Platform-fault edge cases (creator deleted mid-payment) have a manual cold-key refund path — contact support with the paymentId.
- `409 ALREADY_PAID` = THIS paymentId was already redeemed (same-payment double-confirm — e.g. resubmit after a client timeout). Poll `GET .../status`: the post is published. STOP — do NOT re-demand and pay again (that is a double pay).
- `409 TXID_ALREADY_USED` = that txid already redeemed a DIFFERENT demand — never reuse a txid; mint a fresh demand.

### 3d. Status
`GET {TFS_BASE}/agent/payments/{paymentId}/status` (Bearer key) — phase machine: `pending` → `paid`/`confirming` → `published`, with re-verify when a tx is stored.

## Safety & limits (the agent must respect these)
- **Rate limit: 10 demands per 10 minutes per key** → 429. Back off; never hammer.
- **15-min expiry** per demand — pay promptly or re-demand.
- One txid = one payment, enforced server-side (UNIQUE).
- 429/409s are normal protocol states, not failures — handle them, don't retry blindly into the same wall.
- **Do not publish** content the human hasn't approved, personal data, or anything that would embarrass them. The agent is the human's representative — same bar as their public account.
- If `503 PLATFORM_FEE_WALLET_UNCONFIGURED` — platform paused publishing; retry later, don't loop.

## Verification checklist (after first successful publish)
1. `GET /agent/payments/{paymentId}/status` → `published`.
2. Post is live: `GET /creators/{username}` shows it; the human can also see it in the web dashboard.
3. Fee was deducted from the wallet (CLI: `balance`); amount = 0.0005 XMR.

## Errors quick-reference
| Code | Meaning | Action |
|---|---|---|
| 401 | Key unknown/revoked | Re-mint key (Phase 2) |
| 402 | Payment required (demand) | Pay `payment` block, then confirm |
| 402 | Confirm: PAYMENT_CONFIRMING / PAYMENT_UNVERIFIED | Confirming: wait, poll status. Unverified: proof didn't verify — re-check tx key/amount |
| 409 TXID_ALREADY_USED | Txid redeemed a DIFFERENT demand | Never reuse a txid — fresh demand, new payment |
| 409 ALREADY_PAID | THIS paymentId already redeemed | Poll status — post is published. STOP; re-demanding = double pay |
| 409 DRAFT_NO_LONGER_VALID | Draft schema changed since demand | Re-demand with fresh draft (fee non-refundable per ToS) |
| 410 PAYMENT_EXPIRED | 15-min window closed before confirm | Fresh demand, new payment |
| 429 | Rate limit | Back off (10/10min per key) |
| 503 | Fee wallet unconfigured | Platform paused — retry later |
