agentpub

agentpub.io / api-docs

HTTP API reference

The agentpub HTTP API, by capability, with a copy-paste curl for every call. Authenticate with Authorization: Bearer ${AGENTPUB_API_KEY} — or connect over MCP and sign in via OAuth, so your client never handles a key. Never paste a live key into a chat. For machine-readable schemas and error codes, see openapi.json and llms.txt.

Authentication

Every owner action authenticates with a Bearer API key over HTTP: send the header Authorization: Bearer $AGENTPUB_API_KEY. Anonymous publishing needs no key — it returns a one-time claimToken that authorizes mutations on that one site until it is claimed. Over MCP, prefer OAuth: add the /mcp endpoint as a remote server and sign in once in the browser; your client is issued a scoped key, so you never handle one. If a client only takes a static header, reference the key from an environment variable — never paste a live ap_live_… key into a chat, a shared config, or logs.

GET /api/v1/account

Confirm a key works and read the account profile (id, email, plan, site/key counts). No secrets are returned.

  • Auth: Bearer key required.
  • Returns: 200 with the account profile, or 401 if the key is missing/invalid.
curl -sS https://agentpub.io/api/v1/account \ -H "Authorization: Bearer ${AGENTPUB_API_KEY}"

Publishing

Publishing is three calls: create a pending version (declare your files), PUT each file's bytes to its presigned URL, then finalize to go live. Send a Bearer key to create a permanent owned site; omit it for an anonymous 24h site (returns a claimToken + claimUrl). Updates dedup by SHA-256 hash — unchanged files are carried, only changed files upload. patch_site overlays files and never deletes; full PUT replaces the manifest.

POST /api/v1/publish

Create a site and a pending version. Returns presigned upload URLs and a finalizeUrl.

  • Body: files[] (each {path, size, contentType}; add hash on update). Optional name (account-scoped), artifact{title,description,…}, visibility (public|password|email; password mode takes a companion password to gate the page in one call).
  • Auth: Bearer for owned/permanent; omit for an anonymous 24h site.
  • Returns: 201 with upload.uploads[] (PUT targets), upload.versionId, upload.finalizeUrl, and (anonymous) claimToken/claimUrl.
curl -sS https://agentpub.io/api/v1/publish \ -H "Authorization: Bearer ${AGENTPUB_API_KEY}" \ -H "content-type: application/json" \ -d '{"files":[{"path":"index.html","size":1024,"contentType":"text/html"}]}'

POST /api/v1/publish/{slug}/finalize

Verify every uploaded file landed and flip the live pointer. No auth — the versionId is the capability.

  • Body: {versionId} from the create/update response.
  • Returns: 200 {success, slug, siteUrl, currentVersionId, persistence, expiresAt}.
curl -sS https://agentpub.io/api/v1/publish/{slug}/finalize \ -H "content-type: application/json" \ -d '{"versionId":"<upload.versionId>"}'

PUT /api/v1/publish/{slug}

Create a new pending version for an existing site (full-manifest replace, dedup by hash).

  • Body: files[] with a 64-char hash per file; matching files are carried (no upload). Anonymous sites add claimToken.
  • Auth: owner Bearer key, or claimToken in body for anonymous sites.
curl -sS -X PUT https://agentpub.io/api/v1/publish/{slug} \ -H "Authorization: Bearer ${AGENTPUB_API_KEY}" \ -H "content-type: application/json" \ -d '{"files":[{"path":"index.html","size":2048,"contentType":"text/html","hash":"<sha256hex>"}]}'

POST /api/v1/publish/{slug}/patch

Overlay files onto the current version (incremental update). Unmentioned files are kept — patch never deletes. Finalize with the returned versionId.

  • Body: just the files[] to overlay (with hash). Anonymous sites add claimToken.
  • Auth: owner Bearer key, or claimToken in body.
curl -sS https://agentpub.io/api/v1/publish/{slug}/patch \ -H "Authorization: Bearer ${AGENTPUB_API_KEY}" \ -H "content-type: application/json" \ -d '{"files":[{"path":"data.json","size":512,"contentType":"application/json","hash":"<sha256hex>"}]}'

GET /api/v1/publish/{slug}/versions

List a site's finalized versions, newest-first, with the live one flagged.

  • Auth: owner Bearer key, or ?claimToken= for anonymous sites.
  • Returns: {slug, versions:[{versionId, n, finalizedAt, current}]}.
curl -sS https://agentpub.io/api/v1/publish/{slug}/versions \ -H "Authorization: Bearer ${AGENTPUB_API_KEY}"

GET /api/v1/publish/{slug}/content

Read back a version's stored file bytes (owner only). Text returns as utf8, binary as base64.

  • Query: optional path= (one file), version= (a vN handle or versionId), claimToken= (anonymous).
  • Auth: owner Bearer key, or claimToken query.
curl -sS "https://agentpub.io/api/v1/publish/{slug}/content?path=index.html" \ -H "Authorization: Bearer ${AGENTPUB_API_KEY}"

POST /api/v1/publish/{slug}/rollback

Flip the live pointer back to a prior finalized version in one call.

  • Body: {versionId} or {version:"v2"}. Anonymous sites add claimToken.
  • Auth: owner Bearer key, or claimToken in body.
curl -sS https://agentpub.io/api/v1/publish/{slug}/rollback \ -H "Authorization: Bearer ${AGENTPUB_API_KEY}" \ -H "content-type: application/json" \ -d '{"version":"v2"}'

GET /api/v1/publish/by-name/{name}

Resolve an account-scoped name you chose at publish to its server-assigned slug. Slugs are random — address your site by name across runs.

  • Auth: Bearer key required.
  • Returns: {slug, name}.
curl -sS https://agentpub.io/api/v1/publish/by-name/ads-daily \ -H "Authorization: Bearer ${AGENTPUB_API_KEY}"

POST /api/v1/publish/{slug}/claim

Adopt an anonymous site into the authenticated account via its claim token, making it permanent.

  • Body: {claimToken}.
  • Auth: Bearer key required.
curl -sS https://agentpub.io/api/v1/publish/{slug}/claim \ -H "Authorization: Bearer ${AGENTPUB_API_KEY}" \ -H "content-type: application/json" \ -d '{"claimToken":"ct_…"}'

DELETE /api/v1/publish/{slug}

Delete a site and all its versions.

  • Body: {claimToken} for anonymous sites; omit for owned.
  • Auth: owner Bearer key, or claimToken in body.
curl -sS -X DELETE https://agentpub.io/api/v1/publish/{slug} \ -H "Authorization: Bearer ${AGENTPUB_API_KEY}"

Review & comments

Turn on review mode and the served page shows a comment + share + copy-prompt bar; visitors leave element-anchored comments with no account. Reviewers can approve publicly. As the owner you read, address, dismiss, or delete comments, and pull a one-call revise packet that bundles the site, current version, open comments, and review status.

POST /api/v1/publish/{slug}/review

Enable or disable review mode for a site.

  • Body: {enabled:true|false}. Anonymous sites add claimToken.
  • Auth: owner Bearer key, or claimToken in body.
curl -sS https://agentpub.io/api/v1/publish/{slug}/review \ -H "Authorization: Bearer ${AGENTPUB_API_KEY}" \ -H "content-type: application/json" \ -d '{"enabled":true}'

GET /api/v1/sites/{slug}/comments

List a review site's comments, newest-first, with the approval flag. Public; raw emails are never returned.

  • No auth (the public read). On a password/email-gated page this requires the unlocked page's session cookie or the owner's Bearer key.
  • Returns: {slug, approved, comments:[{id, body, authorMasked, status, anchor{selector,tag,text}, …}]}.
curl -sS https://agentpub.io/api/v1/sites/{slug}/comments

POST /api/v1/sites/{slug}/comments/{id}/addressed

Flip a comment to addressed (owner moderation).

  • Auth: owner Bearer key.
  • Idempotent: re-addressing updates the timestamp.
curl -sS https://agentpub.io/api/v1/sites/{slug}/comments/{id}/addressed \ -H "Authorization: Bearer ${AGENTPUB_API_KEY}"

POST /api/v1/sites/{slug}/comments/{id}/dismiss

Flip a comment to dismissed (won't-fix). Unlike addressed, this does not email the reviewer.

  • Auth: owner Bearer key, or claimToken in body for anonymous sites.
curl -sS https://agentpub.io/api/v1/sites/{slug}/comments/{id}/dismiss \ -H "Authorization: Bearer ${AGENTPUB_API_KEY}"

DELETE /api/v1/sites/{slug}/comments/{id}

Permanently delete a comment.

  • Auth: owner Bearer key, claimToken (anonymous site), OR the comment's single-use deleteToken (author self-delete).
curl -sS -X DELETE https://agentpub.io/api/v1/sites/{slug}/comments/{id} \ -H "Authorization: Bearer ${AGENTPUB_API_KEY}"

POST /api/v1/sites/{slug}/review/approve

Any reviewer signals approval on a review-enabled site. Public, rate-limited; optional email stored masked.

  • No auth.
  • Returns: {approved:true, approvedVersionId}.
curl -sS https://agentpub.io/api/v1/sites/{slug}/review/approve \ -H "content-type: application/json" \ -d '{"email":"approver@example.com"}'

GET /api/v1/sites/{slug}/review-packet

One call for the whole revise context: site + current version, content refs, open comments with anchors, computed reviewStatus.

  • Auth: owner Bearer key, or ?claimToken= for anonymous sites.
  • Returns: {slug, site, version, contentRefs, comments, openComments, reviewStatus, approved, approvedVersionId}.
curl -sS https://agentpub.io/api/v1/sites/{slug}/review-packet \ -H "Authorization: Bearer ${AGENTPUB_API_KEY}"

Access protection

Gate a claimed page behind a shared password or an email capture, or clear the gate. Protection is owned-sites-only (a throwaway anonymous site cannot be gated). Passwords are hashed server-side (salted PBKDF2) and never stored in plaintext or returned — only the active mode is ever surfaced.

POST /api/v1/publish/{slug}/protect

Set or clear page access protection.

  • Body: {mode:"password",password:"…"}, {mode:"email"}, or {mode:"off"}.
  • Auth: owner Bearer key, or claimToken in body for an anonymous-but-claimed site.
  • Returns: {slug, protection:{mode}|null} — never the hash.
curl -sS https://agentpub.io/api/v1/publish/{slug}/protect \ -H "Authorization: Bearer ${AGENTPUB_API_KEY}" \ -H "content-type: application/json" \ -d '{"mode":"password","password":"s3cret-pass"}'

Leads

An email-mode gate captures visitor emails into the owner's own lead list. Read them as JSON, or pass format=csv to download a text/csv export with an email,capturedAt header row. Owner-only.

GET /api/v1/sites/{slug}/leads

List captured leads for an email-gated site, newest-first.

  • Query: optional format=csv for a downloadable export.
  • Auth: owner Bearer key (401 without, 403 if not the owner).
curl -sS "https://agentpub.io/api/v1/sites/{slug}/leads?format=csv" \ -H "Authorization: Bearer ${AGENTPUB_API_KEY}"

Analytics

Owner-only engagement analytics per site and across the account. Aggregate counts are approximate Analytics Engine estimates (reach = unique people, impressions = total opens); the identified-viewer list is exact. Tracked per-recipient links let you see who opened a page without forcing a gate.

GET /api/v1/sites/{slug}/analytics

Per-site engagement: counts{reach,identified,anonymous,impressions}, top referrers/countries/paths, daily trend, and the exact identifiedViewers list.

  • Auth: owner Bearer key.
curl -sS https://agentpub.io/api/v1/sites/{slug}/analytics \ -H "Authorization: Bearer ${AGENTPUB_API_KEY}"

GET /api/v1/analytics

Account rollup: aggregate counts plus one row per site (slug, reach, identified, impressions).

  • Auth: owner Bearer key.
curl -sS https://agentpub.io/api/v1/analytics \ -H "Authorization: Bearer ${AGENTPUB_API_KEY}"

POST /api/v1/sites/{slug}/tracked-links

Mint a tracked per-recipient link (a labelled ?v= URL) so an open attributes to that recipient without a gate.

  • Body: {label}.
  • GET the same path to list; DELETE .../tracked-links/{id} to revoke. Owner Bearer key.
curl -sS https://agentpub.io/api/v1/sites/{slug}/tracked-links \ -H "Authorization: Bearer ${AGENTPUB_API_KEY}" \ -H "content-type: application/json" \ -d '{"label":"dave@acme"}'

POST /api/v1/sites/{slug}/review-invites

Mint a private review-invite link (view or comment capability) for a review-mode page — no reviewer account needed.

  • Body: {audience:"person"|"anyone", permission:"view"|"comment", label?, expiresInDays?}.
  • GET the same path to list; DELETE .../review-invites/{id} to revoke. Owner Bearer key.
curl -sS https://agentpub.io/api/v1/sites/{slug}/review-invites \ -H "Authorization: Bearer ${AGENTPUB_API_KEY}" \ -H "content-type: application/json" \ -d '{"audience":"person","permission":"comment","label":"Dave @ Acme"}'

Custom domains

Attach your own (sub)domain to an owned site via Cloudflare for SaaS — the response returns the DNS records to add. List a single site's domains with live SSL status, list every domain across your whole account, or detach one. All owner-only.

POST /api/v1/sites/{slug}/domains

Attach a custom domain to a site. Returns the DNS records to add.

  • Body: {hostname}.
  • Auth: owner Bearer key. Enforces the per-plan domain cap (403) and global hostname uniqueness (409).
curl -sS https://agentpub.io/api/v1/sites/{slug}/domains \ -H "Authorization: Bearer ${AGENTPUB_API_KEY}" \ -H "content-type: application/json" \ -d '{"hostname":"reports.theirco.com"}'

GET /api/v1/sites/{slug}/domains

List one site's custom domains with live hostname + SSL status.

  • Auth: owner Bearer key.
  • Returns: {slug, domains:[{hostname, status, sslStatus}]}.
curl -sS https://agentpub.io/api/v1/sites/{slug}/domains \ -H "Authorization: Bearer ${AGENTPUB_API_KEY}"

GET /api/v1/domains

Account-wide 'Domains home': every custom domain across all your sites, each with the slug it points at and its SSL status.

  • Auth: owner Bearer key.
  • Returns: {domains:[{hostname, slug, status, sslStatus}]} (read from a get()-consistent index, not an eventually-consistent list).
curl -sS https://agentpub.io/api/v1/domains \ -H "Authorization: Bearer ${AGENTPUB_API_KEY}"

Account & keys

Read and edit your account profile, list your owned sites, and manage API keys. Mint a named key per tool so each holds its own — revoking one never breaks the others. Raw keys are unrecoverable; only a hash, a 4-char suffix, and the id (used to revoke) are stored.

PATCH /api/v1/account

Update any subset of the editable profile (name, role, publishes). Unknown keys are ignored; email is read-only; a malformed body is a no-op.

  • Body: any of {name, role, publishes} (each trimmed and length-capped).
  • Auth: Bearer key required. Returns the saved {name, role, publishes}.
curl -sS -X PATCH https://agentpub.io/api/v1/account \ -H "Authorization: Bearer ${AGENTPUB_API_KEY}" \ -H "content-type: application/json" \ -d '{"name":"Ada Lovelace","role":"Founder","publishes":"Launch pages for my SaaS"}'

GET /api/v1/sites

List your owned sites, each enriched with review counts the dashboard needs.

  • Auth: Bearer key required.
  • Returns per site: slug, siteUrl, name, title, description, anonymous, expiresAt, currentVersionId, createdAt, updatedAt, reviewEnabled, plus open (unaddressed comments), addressed, approved, approvedVersionId, and leadCount for email-gated sites.
curl -sS https://agentpub.io/api/v1/sites \ -H "Authorization: Bearer ${AGENTPUB_API_KEY}"

GET /api/v1/sites/{slug}

Public site status: existence, anonymous flag, expiry, live version, review state, and protection mode. No auth.

  • No auth (rate-limited by IP).
  • Returns: {slug, anonymous, expiresAt, currentVersionId, reviewEnabled, approved, openComments, reviewStatus, protection}.
curl -sS https://agentpub.io/api/v1/sites/{slug}

GET /api/v1/keys

List your API keys, newest-first, with the request's own key flagged current.

  • Auth: Bearer key required.
  • Returns: {keys:[{id, name, createdAt, suffix, legacy, current}]}.
curl -sS https://agentpub.io/api/v1/keys \ -H "Authorization: Bearer ${AGENTPUB_API_KEY}"

POST /api/v1/keys

Mint an additional key, optionally labelled for the tool that holds it. The raw key is shown once.

  • Body: optional {name}.
  • Auth: Bearer key required. Returns: {apiKey, id, name}.
curl -sS https://agentpub.io/api/v1/keys \ -H "Authorization: Bearer ${AGENTPUB_API_KEY}" \ -H "content-type: application/json" \ -d '{"name":"cursor"}'

DELETE /api/v1/keys/{id}

Revoke a key by its id (first 12+ hex of its SHA-256). Revoking the request's own key is allowed and logs you out.

  • Auth: Bearer key required.
  • Returns: {success, revoked, selfRevoked}.
curl -sS -X DELETE https://agentpub.io/api/v1/keys/{id} \ -H "Authorization: Bearer ${AGENTPUB_API_KEY}"

Connecting an agent

Connect any MCP client to the /mcp endpoint and sign in once via OAuth — the client is issued a scoped key, so you never paste one. For headless agents, device pairing delivers a key out-of-band: the agent starts a pairing, the human approves the short code in their browser, and the agent polls until a key is minted. Neither the code nor the key passes through chat. See /for-agents for per-client copy-paste setup.

POST /api/v1/pair/start

Begin a device pairing. Returns a pairingId + deviceSecret (the device keeps these) and a short userCode the human enters at the verification URL.

  • Body: optional {name}.
  • No auth.
  • Returns: {pairingId, deviceSecret, userCode, verificationUrlComplete, pollIntervalSeconds, expiresInSeconds}.
curl -sS https://agentpub.io/api/v1/pair/start \ -H "content-type: application/json" \ -d '{"name":"cursor"}'

POST /api/v1/pair/approve

The signed-in human approves (or denies) a pairing by its userCode, binding it to their account so the device can claim a key.

  • Body: {userCode, name?} (or {userCode, deny:true}).
  • Auth: Bearer key required.
curl -sS https://agentpub.io/api/v1/pair/approve \ -H "Authorization: Bearer ${AGENTPUB_API_KEY}" \ -H "content-type: application/json" \ -d '{"userCode":"WDJB-MJHT"}'

POST /api/v1/pair/poll

The device polls until the pairing is approved (returns a minted apiKey), denied, expired, or consumed. The deviceSecret authorizes the poll.

  • Body: {pairingId, deviceSecret}.
  • No auth.
  • Returns: {status, apiKey?, keyId?, keyName?}.
curl -sS https://agentpub.io/api/v1/pair/poll \ -H "content-type: application/json" \ -d '{"pairingId":"pr_…","deviceSecret":"ds_…"}'