# VOTARO Showcase — Agent Guide

Publish a listing on the VOTARO showcase (https://www.votaro.app/showcase) via the agent API. This guide gives an agent the exact flow and contracts — no account, no CAPTCHA, no human step until the final ownership confirmation.

Base URL: `https://www.votaro.app`

## Machine-readable endpoints (read, no auth)

Before submitting, you can read the whole catalog:

- `GET /llms.txt` — overview + link index
- `GET /llms-full.txt` — the overview plus the entire catalog inline
- `GET /api/catalog.json` — board + showcase in one JSON document
- `GET /api/v1/board` — ranked leaderboard items (`?category=<slug>`, `?limit=<n>`)
- `GET /api/v1/listings` — approved showcases
- `GET /openapi.json` — OpenAPI 3.1 spec for this whole surface
- `POST /mcp` — MCP JSON-RPC 2.0 server (`tools/list`, then `tools/call` with `search_apps` / `get_app` / `top_apps` / `list_showcases` / `list_categories`)

All are CDN-cached — poll sparingly.

## Lifecycle

```
draft (created via API) → pending (human confirmed via magic link) → approved (admin) → visible on /showcase
```

Only approved listings with a stored screenshot appear on the gallery.

## Step 1 — Register an agent key

`POST /api/v1/register` — no auth.

```bash
curl -X POST https://www.votaro.app/api/v1/register \
  -H "content-type: application/json" \
  -d '{"name":"My App"}'
```

Response `201`:

```json
{ "apiKey": "vot_agent_<b64url>", "claimUrl": "/api/v1/listings" }
```

- `name` is required (trimmed, max 120 chars). `description` and `url` are optional.
- The `apiKey` is shown **exactly once** — only its SHA-256 hash is stored. Save it immediately; it cannot be recovered.
- Errors: `400 bad_request` (no name), `429 rate_limited` (5/hr per IP, 20/day global).

## Step 2 — Create a draft listing

`POST /api/v1/listings` with `Authorization: Bearer <apiKey>`.

```bash
curl -X POST https://www.votaro.app/api/v1/listings \
  -H "authorization: Bearer <apiKey>" \
  -H "content-type: application/json" \
  -d '{"targetUrl":"https://myapp.com"}'
```

Response `201`:

```json
{ "ok": true, "id": "<uuid>", "status": "draft", "confirmUrl": "/showcase/<slug>/confirm?token=<token>" }
```

- `targetUrl` is required (http/https, public hostname — IP literals and localhost rejected). Title and description are **auto-fetched** from the target site's `<title>` and meta/og:description — you only supply the URL. If the page has no title, the domain is used.
- VOTARO screenshots your site in the background (external renderer, cookie banners + ads stripped) and stores it.
- Errors: `401 unauthorized` (bad/missing key), `409 already_listed`, `422 invalid_url`, `429 rate_limited` (30/min per key).

## Step 3 — Confirm ownership (HUMAN step)

Open the `confirmUrl` from step 2 in a browser. A **human** must open it and solve the Turnstile challenge. This flips the draft to `pending` and sends it to the review queue.

- This is deliberately human-only: the magic link is gated by Cloudflare Turnstile, so an agent holding the link cannot self-publish with curl.
- Hand the `confirmUrl` to the human owner of the site.

## Step 4 — Get reviewed & go live

No agent action needed. The VOTARO team reviews the pending listing and approves it. Once approved, it appears on `/showcase` with its vote button.

- After approval, the listing is publicly readable via `GET /api/v1/listings/<id>` and ranked in `GET /api/v1/listings`.

## Deleting a listing

`DELETE /api/v1/listings/<id>` — auth via the creating API key (Bearer) or the magic-link token (`?token=`). Only the creator can delete.

## Key facts & pitfalls

- The showcase is fully independent of the leaderboard. Do **NOT** use `/submit` or `/api/submit` — that adds to the main board, not the showcase.
- The `apiKey` is one-time and unrecoverable — persist it the moment you get it.
- The confirm step is deliberately human-only. Do not attempt to bypass Turnstile; route the magic link to the human owner.
- Screenshot capture is async — a freshly created listing may not be visible on `/showcase` for a short time even after approval.
- Rate limits: 5 registrations/hr per IP (20/day global), 30 requests/min per key.
