Polls — Reference
The Polls surface owns multiple-choice questions that appear on public pages, capture visitor responses, aggregate the totals, and render the results as a chart. A poll has a stable id, a question, a list of choices, a response store, a visualization mode, and an optional expiration. Anything that asks a visitor a question and counts their answer traces back to a record managed here.
This page is a reference for platform engineers and integrators who need to understand the surface before extending it, scripting against it, or building a survey-style integration. Customer-facing how-tos live in the customer docs set; this page describes the shape of the surface, not the steps to drive it.
Overview
Polls live under the Polls module in SG-Admin. The module renders four primary views — the poll list, the poll create / edit form, the response inspector, and the results visualization preview — and exposes write operations for create, edit, duplicate, open, close, reset responses, archive, and delete.
A poll record carries a question, two or more answer choices, a response-capture policy (one response per session, one per IP, one per signed-in user), and a visualization mode (horizontal bar, vertical bar, donut, raw counts). The active state of a poll is one of: scheduled, open, closed, or archived. A poll moves between states either manually from the edit form or automatically based on the schedule and expiration fields.
Responses arrive via a public submit endpoint when the poll is embedded on a page. Each response writes a row to the poll's response store carrying the chosen option, the response timestamp, the dedup key (session, IP, or user id depending on policy), and an optional comment if the poll allows comments. Aggregates are computed from the response store on read.
Where it lives in SG-Admin:
- Sidebar: SG-Admin → Polls
- URL prefix:
/sg-admin/polls/ - View templates:
application/views/Admin/Polls/
┌──────────────────────────────────────────────────────────────────────┐│ SG-Admin → Polls [+ New poll]├──────────────────────────────────────────────────────────────────────┤│ Question State Responses Expires│ ────────────────────────────────────── ──────── ────────── ────────│ What stack are you migrating from? Open 1,847 —│ Which feature do you use most? Open 612 2 weeks│ Did the new editor help? Closed 2,304 ended│ Holiday color preference Scheduled 0 opens 12/01│ ││ [⋯ Edit] [⋯ Duplicate] [⋯ Open/Close] [⋯ Responses] [⋯ Archive] │└──────────────────────────────────────────────────────────────────────┘Actions
The Polls surface exposes the following operations. Each is described by what it does to the data, not by its internal method name.
List and search
Returns the poll records on the site, with question, state, response count, and expiration columns. Supports column sort, free-text filter on the question text, and a state filter (scheduled, open, closed, archived). Driven by the standard SG-Admin data-table contract — responses arrive as a row collection plus a total count.
Create poll
Opens an empty poll form. Required at minimum: question, two or more choices, response policy. Optional: open-at date, expires-at date, visualization mode, allow-comments flag, public-results flag. Submit validates that no two choices carry the same label, then persists the record.
Edit poll
Loads an existing record into the form, pre-populated. Submit replaces the stored question, choices, and policy. Editing choices on a poll that already has responses is allowed but flagged in the form; the surface shows the response count per choice so the operator can decide whether to keep or reset the existing responses.
Duplicate poll
Clones an existing record. The clone carries the suffix (copy) on its question, lands in scheduled state with the open-at and expires-at fields cleared, and has its response store reset. Useful for repeating a poll on a new campaign or for an A/B variant.
Open / Close
Manually transitions the poll state. Open moves the poll from scheduled or closed into open and unlocks the public submit endpoint. Close moves the poll from open into closed and locks the submit endpoint while keeping the response store and results visualization available.
Reset responses
Wipes the response store for the poll while preserving the question, choices, and policy. The previous response count is recorded in an Activity Log entry; the wipe itself is irreversible.
Response inspector
Returns the raw response rows for the poll, paginated, with timestamp, dedup key, chosen option, and comment columns (if comments are enabled). Supports CSV export. Useful for cross-checking aggregate counts against raw rows and for spot-checking comment content.
Archive
Sets the poll state to archived. Archived polls do not accept responses and are hidden from the embed picker, but their response store and results visualization remain available for read.
Delete
Hard-removes the poll record and its response store. The surface refuses the action while the poll is embedded on any active page; the operator must remove the embedding surfaces before delete is allowed.
Data model
A poll record carries the following fields. Field names below are the conceptual shape — on-disk column names match closely but are not contractually stable across releases.
| Field | Type | Notes |
|---|---|---|
id | integer | Primary key. Stable across edits. |
slug | string | URL-safe handle. Unique within the site. |
question | string | The poll question. Plain text. |
choices | array | Ordered list of choice objects, each carrying a label and a stable id. |
state | enum | scheduled, open, closed, archived. |
policy | enum | Response policy — session, ip, user. |
allow_comments | boolean | If true, response rows can carry a free-text comment. |
public_results | boolean | If true, results render to visitors after they vote; if false, results stay admin-only. |
visualization | enum | bar-horizontal, bar-vertical, donut, counts. |
open_at | timestamp | Optional. Auto-opens the poll at this time. |
expires_at | timestamp | Optional. Auto-closes the poll at this time. |
created_at | timestamp | Set on create, immutable. |
updated_at | timestamp | Touched on any edit. |
poll_id, choice_id, timestamp, dedup_key, optional comment. The dedup key is the session id, IP address, or user id depending on the poll's policy. The submit endpoint refuses a second response with the same dedup key.Aggregate computation. Aggregates are computed at read time from the response store — per-choice count, total count, percent share. The values are not cached; large response volumes use a backing-store index keyed on (poll_id, choice_id).
POLL RECORD├── id integer primary key├── slug string url-safe handle├── question string plain text├── choices[]│ ├── id string stable per-choice id│ └── label string display├── state enum scheduled | open | closed | archived├── policy enum session | ip | user├── allow_comments boolean├── public_results boolean├── visualization enum bar-horizontal | bar-vertical | donut | counts├── open_at timestamp? optional auto-open├── expires_at timestamp? optional auto-close└── timestamps created_at, updated_atPOLL RESPONSE RECORD├── id integer primary key├── poll_id integer parent reference├── choice_id string which choice├── timestamp timestamp when submitted├── dedup_key string session | ip | user id└── comment string? optional free-textPermissions
Access to the Polls surface is gated at two layers.
Layer 1 — admin gate. Every action under SG-Admin passes through the platform's standard admin access check at request entry. An unauthenticated request never reaches the Polls surface. The public submit endpoint is the one exception — it accepts unauthenticated requests but enforces the per-poll dedup policy.
Layer 2 — per-action capability. Each Polls action checks a capability associated with the calling operator's role. The default role configuration ships with three roles — Administrator, Editor, Viewer — and the capability map is:
| Capability | Administrator | Editor | Viewer |
|---|---|---|---|
| List polls | ✔ | ✔ | ✔ |
| Create poll | ✔ | ✔ | — |
| Edit poll | ✔ | ✔ | — |
| Duplicate poll | ✔ | ✔ | — |
| Open / Close manually | ✔ | ✔ | — |
| Reset responses | ✔ | — | — |
| Response inspector | ✔ | ✔ | ✔ |
| Export responses (CSV) | ✔ | ✔ | — |
| Archive | ✔ | ✔ | — |
| Delete | ✔ | — | — |
Embed guard. A poll cannot be deleted while it is embedded on an active page. The surface lists the blocking embeds in the rejection payload.
Audit. Every administrative write — create, edit, open, close, reset, archive, delete — emits an Activity Log entry naming the acting operator, the target record, and the change shape. Public response submissions are logged in the poll's own response store, not the Activity Log.
RESULTS — "What stack are you migrating from?" 1,847 responses──────────────────────────────────────────────────────────────────────WordPress + Elementor ████████████████████████████████ 742 (40%)Squarespace ███████████████████ 369 (20%)Webflow ██████████████ 295 (16%)Wix ████████████ 240 (13%)Custom / hand-rolled █████████ 149 ( 8%)Other ███ 52 ( 3%)State: open · Policy: one per session · Public results: visibleRelated references
- Forms — Reference. Multi-question survey workflows live under Forms. Polls is single-question, count-based.
- Pages — Reference. Pages embed polls via the standard block-pick field; the page renderer handles the submit endpoint and the results visualization.
- Posts — Reference. Posts embed polls the same way pages do.
- Activity log — Reference. Every administrative poll write logs there; response submissions log to the poll's own response store.
- Settings — Reference. Default response policy, default visualization mode, and the response-store retention window live under Settings.
- Analytics — Reference. Poll embed views and response submissions appear in the standard analytics event stream as
poll.viewandpoll.respond.
/docs/engagement/polls.Response flow and dedup behavior
When a visitor submits a response, the platform walks a four-step pipeline. Understanding the order matters for any integration that builds custom poll embeds or that audits response volumes.
- State check. The submit endpoint first verifies the poll's state. Submissions to a scheduled, closed, or archived poll are rejected with a structured payload naming the state.
- Dedup lookup. The poll's response store is checked for an existing row with the same dedup key. If a row exists, the endpoint returns the existing response (the visitor sees their prior vote) without writing a new row.
- Choice validation. The submitted choice id is verified against the poll's current choice list. Submissions referencing a choice that was removed in a recent edit are rejected with a structured payload naming the missing choice.
- Persist and aggregate. A new response row is written. The aggregate index is updated. The endpoint returns the updated counts and the visitor's chosen option.
Comment moderation. Polls that allow comments expose a moderation queue under the response inspector. New comments default to a pending state and require an operator approval before rendering on the public results visualization. The moderation queue is the same queue used by the Discussions surface, so the moderation skill set transfers.
Result visibility. A poll's public_results flag controls whether the results visualization renders to a visitor after they vote. With the flag off, the visitor sees a confirmation message but no aggregate; only signed-in operators see the results from the admin response inspector.
Retention window. Response rows are retained until the poll is reset or deleted. Settings → Polls exposes a retention cap (default: indefinite); enabling the cap auto-archives response rows older than the configured window.
