# greatfeedback.ai — agent guide (full) This file is the long-form companion to `/llms.txt`. Paste it into your context window to operate against the API without scraping HTML. ## Auth Two credential shapes; both go in the standard `Authorization` header. - `Bearer gfat_` workspace-scoped API token. Mint via OWNER session at `POST /api/workspaces/{ws}/api-tokens`. Tokens are clamped to MEMBER or GUEST at issuance. - `Bearer ` human sessions; not generally available to agents. `gfat_*` tokens carry their workspace internally — no `X-GF-Workspace` header needed. ## Headers | Header | When | Purpose | |---------------------|--------------|---------| | `Authorization` | always | bearer credential | | `Idempotency-Key` | POST writes | recommended for retries | | `X-Request-Id` | optional | echoed in logs for correlation | ## Status taxonomy `new | ack | triaged | in_progress | blocked | resolved | won_t_fix | duplicate` Unknown values 400 at validation. `resolved` and `won_t_fix` should carry a `summary` describing the outcome. ## Auto-fix and resolution evidence Each annotation carries an `auto_fix: bool` flag set at submission time. When `true`, the submitter has authorised the consumer system (you, an agent, or a CI bot) to apply the change without further discussion. Anonymous submitters always land `false` regardless of what the client sent — the server coerces. Stakeholder tokens have a per-row `autoFixDefault` (`never | optional_off | optional_on`) that gates whether their submissions can flag the toggle at all. To pull only the auto-fix work: ``` GET /api/v1/sites/{site_id}/annotations?auto_fix=true ``` When you ship a fix, attach a `RESOLUTION` row to the annotation. At least one of `before_key`, `after_key`, `diff_url`, `explanation` must be present. `diff_url` must be `https://`. `explanation` is markdown, 4 KB cap. Multiple resolutions per annotation are allowed (fix → revert → re-fix); the list reads back in creation order. ``` # 1. (optional) Get a presigned PUT for a screenshot POST /api/v1/annotations/{annotation_id}/resolutions/upload-url { "kind": "before" | "after", "content_type": "image/png" } → { url, method: "PUT", headers, key, expires_in_seconds } # 2. PUT the bytes to that URL with the returned headers PUT body: # 3. Attach the resolution POST /api/v1/annotations/{annotation_id}/resolutions { "before_key": "", "after_key": "", "diff_url": "https://github.com/owner/repo/pull/123", "explanation": "Increased contrast on the primary CTA from #888 to #000." } → { "id", "annotation_id", "actor_id", "actor_kind": "human" | "agent" | "mcp", "before_url", "after_url", "diff_url", "explanation", "notification_status": "pending" | "sent" | "queued_reply" | "failed", "created_at", "notified_at" } # 4. List the fixes already attached GET /api/v1/annotations/{annotation_id}/resolutions → [ ResolutionResponse, ... ] ``` Notification fan-out is async. The submitter gets: - An email (Resend `feedback_resolved` template, before/after inlined) when the annotation has `authorEmail` on file. - A REPLY row on the annotation thread when they're anonymous, so the next time they revisit the page the widget surfaces the resolution. `notification_status` reports the outcome on the row. ## Pagination List endpoints return `{ items, next_cursor }`. Pass `?cursor=` to continue. `next_cursor: null` means last page. Cursors are HMAC-signed; tampering returns 400. ## Rate limits - per token: 600 / 60s - per IP: 30 / 60s 429 responses include a `reason` string. There is no automatic retry; respect the window. ## Demo endpoint ``` POST /api/demo/run { "url": "https://example.com", "persona_slug": "designer" } ``` Returns top-3 dimensions + 1-paragraph paragraph + cta to sign-up. Anonymous caller: 1 call / IP / 24h. Personas restricted to: `designer`, `marketer`, `copy-editor`. Server fetches the URL, extracts text, runs Haiku 4.5. ## Agent self-signup (PRIVATE BETA — currently closed) `POST /api/v1/agents/register` currently returns 403 with body `{ "reason": "private_beta", "message": "..." }`. Pre-beta agents are provisioned by hand: have your operator request access at https://greatfeedback.ai/signup; we mint the gfat_* token and reply by email. The future contract (when the gate flips) is documented below. ``` POST /api/v1/agents/register { "agent_name": "string", # opaque identifier; for your audit log "contact_email": "string", # required so we can reach a human if # the agent goes off the rails "purpose": "string" # optional, free-form } ``` Returns (post-beta): ``` { "workspace_id": "ws_...", "token": "gfat_...", # shown ONCE; persist immediately "billing_mode": "prepaid_micro", "per_review_price_cents": 5, "wallet_topup_url": "https://...", "wallet_balance_cents": 0 } ``` The workspace is created with `plan = "agent"`. No subscription. Each `run_personas` call deducts `per_review_price_cents` from the wallet. Calls when the wallet is empty return 402 with a `wallet_topup_url`. ## MCP discovery ``` GET /mcp.json -> { "mcp_version": "0.1", "endpoint": "https://api.greatfeedback.ai/mcp/", "tools": [ { "name": "list_annotations", "auth": "mcp_token" }, { "name": "read_annotation", "auth": "mcp_token" }, { "name": "acknowledge", "auth": "mcp_token" }, { "name": "reply", "auth": "mcp_token" }, { "name": "resolve", "auth": "mcp_token" }, { "name": "patch_annotation", "auth": "mcp_token" }, { "name": "get_resolution_upload_url", "auth": "mcp_token" }, { "name": "attach_resolution", "auth": "mcp_token" }, { "name": "list_resolutions", "auth": "mcp_token" }, { "name": "run_personas", "auth": "mcp_token" } ], "discovery": "https://api.greatfeedback.ai/openapi.json" } ``` ## Conventions - All times are epoch milliseconds (string in JSON for safety against 64-bit precision loss in browsers). - All ids are ULIDs. - Errors return `{ "detail": "..." }` with the appropriate HTTP code. - 401 includes `WWW-Authenticate: Bearer error="invalid_token"`. ## Quick start for an agent ``` TOKEN=$(curl -s -X POST https://api.greatfeedback.ai/api/v1/agents/register \ -H "Content-Type: application/json" \ -d '{"agent_name":"my-agent","contact_email":"you@example.com"}' \ | jq -r .token) curl -H "Authorization: Bearer $TOKEN" \ https://api.greatfeedback.ai/api/v1/sites ```