# Publr Agent API Full Context Publr is a publishing platform for turning files and generated web pages into public URLs. This agent surface is additive: it reuses Publr's existing project, asset, R2 upload, security scan, deployment, and public URL flow. ## Account Self-Signup API An agent can provision (or re-authenticate) its own Publr account from just an email address — no dashboard, no human. Both endpoints are UNAUTHENTICATED, rate-limited, and held to a constant-timing floor. `POST /api/v1/agent/auth/request-code` — body `{ "email": "you@example.com", "language": "en-US" }` (`language` optional). It ALWAYS returns `202 { "status": "code_sent" }` — byte-identical and latency-identical whether or not the email already has an account. This is deliberate: the endpoint cannot be used to enumerate which emails have Publr accounts. `POST /api/v1/agent/auth/verify-code` — body `{ "email": "you@example.com", "code": "ABC-234" }`. The code is the human-readable string from the email (unambiguous alphabet; trimmed and uppercased server-side). On success: ```json { "apiKey": "pk_live_...", "tokenPreview": "pk_live_pub_...wxyz", "clerkUserId": "user_...", "created": true } ``` `apiKey` is a standard revocable `pk_live_*` agent token, returned exactly once — store it immediately. `created` is `true` for a brand-new account, `false` when an existing one was matched. Every bad-code reason (not found / expired / already used / wrong / locked) collapses to one generic `400 { "error": "invalid_code", "code": "invalid_code" }`. Clerk remains the sole identity provider — the email code is never a credential, and is never accepted as a bearer token. ## Authentication Token management routes use a Clerk bearer JWT: - `GET https://api.publr.host/api/v1/agent/tokens` - `POST https://api.publr.host/api/v1/agent/tokens` - `POST https://api.publr.host/api/v1/agent/tokens/{id}/revoke` Agent routes use a Publr agent token in the `Authorization` header: ```http Authorization: Bearer pk_live_... ``` Agent tokens are opaque, revocable, hash-verified server-side, and converted into short-lived internal Convex JWTs by the API Worker after verification. ## Token Management API `GET /api/v1/agent/tokens` lists caller-owned token previews for a Clerk-authenticated user. Query parameters: - `pageSize`: optional integer, default 20, maximum 100. - `cursor`: optional pagination cursor from the previous response. Successful response: ```json { "page": [ { "tokenId": "agentTokens:...", "publicId": "pub_...", "tokenPreview": "pk_live_pub_...wxyz", "tokenPrefix": "pk_live_", "tokenSuffix": "wxyz", "label": "local agent", "scopes": ["publish:write"], "status": "active", "createdAt": 1700000000000 } ], "isDone": true, "continueCursor": "" } ``` The list response never includes the raw token secret or token hash. ## CLI The CLI package is `@publr/cli` and exposes the `publr` binary. `publr signup` / `publr login` run the account self-signup flow end to end — they POST `request-code`, prompt for the emailed code (or take `--code`), POST `verify-code`, and persist the returned `apiKey` to `~/.publr/credentials`. They are ergonomic aliases of one another (the server does create-or-find): ```sh npx @publr/cli signup --email you@example.com --api-url https://api.publr.host ``` ```sh PUBLR_API_URL=https://api.publr.host \ PUBLR_API_TOKEN=pk_live_... \ npx @publr/cli whoami ``` ```sh PUBLR_API_URL=https://api.publr.host \ PUBLR_API_TOKEN=pk_live_... \ npx @publr/cli publish ./index.html \ --slug agent-demo \ --name "Agent demo" \ --content-type text/html \ --language en-US ``` `--api-key`, `--title`, and `PUBLR_API_KEY` are accepted aliases for `--token`, `--name`, and `PUBLR_API_TOKEN` respectively. When both env vars are set, `PUBLR_API_TOKEN` wins. The CLI prints JSON on stdout (progress notices and prompts go to stderr). `publr signup` / `publr login` persist the returned `apiKey` to `~/.publr/credentials` (mode `0600`). ## Publish API `POST /api/v1/agent/publish` publishes one inline UTF-8 file. It requires: - `Authorization: Bearer ` - `Idempotency-Key: ` - `publish:write` token scope Request body: ```json { "name": "Agent demo", "slug": "agent-demo", "filename": "index.html", "contentType": "text/html", "content": "

Hello

", "sessionFingerprint": "11111111-1111-4111-8111-111111111111", "language": "en-US" } ``` Successful response: ```json { "projectId": "projects:...", "workspaceId": "workspaces:...", "assetId": "assets:...", "deploymentId": "deployments:...", "hostname": "agent-demo.publr.site", "sandboxUrl": "https://agent-demo.publr.site" } ``` ## Safety Boundaries - Do not send `userId`; Publr derives ownership from verified auth. - Do not log raw agent tokens. - Use idempotency keys for publish retries. - Use `GET /api/v1/agent/tokens` for token inventory; store the raw token only when it is first created. - Treat frontend plan guards as UX only; backend checks enforce limits. ## Not Implemented In This Slice - Multi-file Sites. - Drive-style APIs. - Dashboard token management UI. - Payments or billing.