Panel CommentsAdmin sign in
Documentation

Browser API

HTTP API

The widget is the supported browser client. All browser API requests go to the physical api/index.php gateway and require the embedding page’s exact registered Origin.

Reads

  • GET api/index.php?action=bootstrap&page_url=…&page_key=…
  • GET api/index.php?action=thread&page_url=…&sort=newest|oldest|top&after=…&rv=…
  • GET api/index.php?action=replies&root_id=…&page_url=…&after=…&rv=…
  • GET api/index.php?action=search&page_url=…&q=…&after=…&rv=…
  • GET api/index.php?action=all-comments&page_url=…&after=…&rv=…
  • GET avatar.php?id=COMMENT_ID for the optional locally cached Gravatar image
  • POST api/index.php?action=counts with {"pages":["https://…"]}
  • GET feed.php?site=SITE_ID&page=PAGE_ID for optional RSS

Writes

First call POST api/index.php?action=form-ticket with the page URL/key and an action such as comment_create, comment_reply, comment_edit, comment_delete, comment_reaction, page_reaction, or comment_report. For comment, identity, and reaction writes, the supported widget also returns the antispam.browser_cookie proof from bootstrap through the browser_cookie field after a successful first-party cookie round trip. Required-cookie mode rejects the ticket request when that round trip is absent; adaptive mode retains its normal moderation behavior for comments. comment_like remains a compatibility action for older clients.

  • POST api/index.php?action=comment
  • PATCH|DELETE api/index.php?action=comment&id=COMMENT_ID
  • POST|DELETE api/index.php?action=like&id=COMMENT_ID
  • PUT|DELETE api/index.php?action=reaction&id=COMMENT_ID
  • PUT|DELETE api/index.php?action=page-reaction
  • POST api/index.php?action=markdown-preview
  • POST api/index.php?action=report&id=COMMENT_ID
  • POST api/index.php?action=state
  • POST api/index.php?action=subscribe
  • POST api/index.php?action=identity-exchange
  • POST api/index.php?action=account-claim-password
  • POST api/index.php?action=account-login-password
  • POST api/index.php?action=account-recover-password
  • PATCH api/index.php?action=account-profile
  • POST api/index.php?action=account-email

Send X-Panel-Visitor and, when available, X-Panel-Identity. Every write body includes the one-use form_ticket, a UUID request_id, and a turnstile_token only when the ticket requires it. Comment creation sends a honeypot object keyed by the randomized name in the ticket.

Success responses use {"data":…}. Failures use {"error":{"code":"…","message":"…","details":{},"request_id":"…"}}. Clients should branch on code rather than message text.

Legacy /api/v1/... routes remain available only on servers already configured with front-controller rewriting; new integrations should use the physical gateway.

Bootstrap is personalized and returns page.read_version. Pass that signed token as rv on thread, replies, search, and load-all reads. Current versioned reads return public cache headers, an ETag, and the current token in data.read_version; tokenless reads remain compatible but are no-store. A stale token returns 409 read_version_changed with the replacement token in error.details.read_version, after which the same cursor can be retried. Mutations also return the replacement token. The supported widget handles this automatically.

Public search is scoped to one discussion and accepts a 2–200 character q. Whitespace separates terms, quotation marks preserve a phrase, every term must match, and matching is literal and case-insensitive under the database's normal Unicode collation. Responses contain up to 20 results by default (50 maximum), safe plain-text excerpts, and the published/deleted ancestor path needed to understand each match. The opaque after cursor retrieves the next result page. Search reads are cached and independently rate limited.

all-comments returns a flat, stable-ID stream of up to 200 published comments or visible deleted placeholders per request. Its opaque after cursor is deliberately independent of page cache versions so a large transfer can continue while the discussion is active. Clients must rebuild parent/root relationships from parent_id and root_id; the supported widget does this progressively and preserves the normal paginated view if loading is cancelled or fails.

The bootstrap response includes site.built_in_styles, site.style_preset, and assets.site_css. When built-in styling is enabled, the supported widget uses its isolated base stylesheet followed by the versioned per-website preset, colors, and custom CSS from assets.site_css. When it is disabled, assets.site_css is empty, the widget requests no CSS, and it renders into the custom element's light DOM for styling by the embedding website. The antispam object describes the configured cookie mode, the signed cookie proof to round-trip, and whether the randomized honeypot is enabled.

When Gravatar is enabled for the website, bootstrap includes site.gravatar: true and features.email_input: true. Published comment objects include a local avatar_url. That URL contains the public comment ID—not an email hash—and returns either a cached image or HTTP 404 so clients can retain their initials fallback.

Bootstrap also includes the current participant state and the website's public identity policy. Account writes use form-ticket actions identity_claim, identity_login, identity_recover, identity_update, or identity_email. Password claims return the identity session token plus ten one-use recovery codes. Save those codes immediately; the API never returns them again unless the signed-in participant explicitly regenerates them.

Published comment objects include number, author_handle, author_kind, reserved, and guest_tag. number is the permanent discussion-local reference shown as #14; it does not change when the discussion is sorted or a comment is moderated or deleted. A claimed handle is always public. guest_tag is null when guest labels are disabled, otherwise it is a stable non-secret label scoped to the website or discussion according to the administrator setting.

Bootstrap publishes the fixed reaction catalog with each choice's emoji, label, and current enabled state, plus the website's guests or claimed access rule. Page and comment objects contain aggregate reactions maps and private viewer_reactions lists. Add or remove exactly one reaction by sending reaction, page_url, optional page_key, the matching one-use ticket, browser proof during ticket acquisition, Turnstile token when requested, and a UUID request ID. A visitor may select multiple different reaction types on one target but only one of each type. Disabled historical reaction totals remain readable but cannot be added. The old Like endpoint maps to thumbs_up.

Markdown preview accepts {"body":"…"} from an authenticated visitor, is separately rate limited, and returns the exact sanitized HTML/plain text and renderer version used when posting. Supported comment Markdown includes headings, emphasis, strikethrough, safe autolinks, lists, quotes, tables, inline/fenced code, and horizontal rules. Raw HTML, images, iframes, scripts, embedded media, and task-list inputs are removed. Stored comments record a renderer version; owners can rebuild older stored HTML under Tools → System or with php bin/panel comments:rerender.

Webhooks

Webhook bodies contain id, type, created_at, site_id, and event data. Verify X-Panel-Signature as lowercase HMAC-SHA256 over timestamp + "." + raw_body with the endpoint secret, reject stale timestamps, and deduplicate X-Panel-Delivery.