Embedding pages

One snippet turns any static page into structured intake: your marketing site, a client's WordPress blog, a Notion-published page, the plain HTML brochure site an agency shipped in 2019. Visitors see a page; you see records flowing onto a board, triggering webhooks, ready for your team or an AI agent — with zero backend on the embedding site.

How it works

Each page in Snapi has a public submission endpoint:

POST https://api.snapi.ca/api/v1/public/submit/:entityId

The embed script renders your page automatically from its field schema and posts to this endpoint. Records appear in your workspace in real time.

Getting the embed snippet

  1. Open your workspace and navigate to Pages.
  2. Click the page you want to embed.
  3. Click the Share / Embed button in the top-right toolbar.
  4. Copy the snippet from the Embed tab.

It's a self-sizing iframe — no script tag with data attributes, no build step:

<iframe
  id="snapi-embed-..."
  src="https://app.snapi.ca/embed/your-org/contact-form"
  width="100%"
  height="480"
  frameborder="0"
  scrolling="no"
  style="border: none; border-radius: 8px;"
  title="Contact Form"
></iframe>
<script>
  // Listens for the embedded page reporting its own content height,
  // so the iframe grows/shrinks with the form and never shows its
  // own scrollbar. Included automatically when you copy the snippet.
</script>
i
Always copy the snippet from the app rather than constructing the URL by hand — the org slug in the path matters (page slugs are only unique within your organization).

No website? Use the standalone hosted page

Every public page also has its own real, shareable web page — not an iframe, not a snippet, just a link:

https://app.snapi.ca/forms/your-org/contact-form

It's a full page with your org name, the page's icon/color/description, and the page itself — styled the same way as an embed (see Styling below). Unlike the iframe embed URL, it's indexable and carries Open Graph tags for link previews, so it's meant to be shared directly: an "Apply Now" button in a job listing, a link in an email, a QR code on a poster. Get the link from the page's Share / Embed panel, or replace /embed/ with /forms/ in an iframe URL you already have.

Restricting where your page can be used

Set Allowed Domains in the page's Public Page tab (e.g. example.com, another.com) to control where the page works. Enforcement happens in two places:

Subdomains are included automatically (example.com also allows www.example.com). Leave the field empty to allow embedding anywhere.

Gotchas

Styling the embedded page

snapi.js renders plain, real DOM into your page — no shadow root, no build step. That cuts both ways: your site's CSS can't accidentally clash with it (everything is scoped under the .snapi-form class), but it also means your CSS can reach in and restyle anything, as long as it loads after snapi.js or uses higher specificity.

Colors: the six CSS custom properties

Every color in the default styles reads from a CSS custom property with a hardcoded fallback, so overriding six variables reskins the whole page — inputs, buttons, links, focus rings, everything:

:root {
  --snapi-primary: #2551ed;   /* buttons, links, focus ring, submit */
  --snapi-text:    #111827;   /* labels, title, radio text */
  --snapi-bg:      #ffffff;   /* form background */
  --snapi-border:  #d1d5db;   /* input borders, table header rule */
  --snapi-danger:  #ef4444;   /* danger buttons, retry button */
  --snapi-success: #10b981;   /* success icon */
}
i
You don't need custom CSS to set these — the Theme picker in the page builder's Public Page tab does exactly this from a color-swatch UI, no code required. Reach for the properties above only when you need something the picker doesn't expose, or you're theming from your own site's stylesheet instead of the builder.
!
If a page has a Theme assigned in the builder, snapi.js injects its own <style> block defining these same six variables at :root — added to the page after your own stylesheet, so it wins. Either style purely through the Theme picker, or leave the page's Theme unset and drive colors entirely from your own CSS.

Everything else: the full class reference

For layout, spacing, and anything the six color variables don't cover, target these classes directly. Every field type, button, and state snapi.js can render has one.

Structure & typography

Class What it is
.snapi-formThe root container — font, base text color, padding
.snapi-title / .snapi-subtitleAuto title (or a Title/Subtitle field's rendered text)
.snapi-fields, .snapi-cols-1/2/3The field grid and its column-count variants (stack to 1 column under 640px)
.snapi-fieldOne field's wrapper (label + input); .snapi-field label for the label text
.snapi-requiredThe red * on required fields
.snapi-hr, .snapi-field-dividerA Divider field
.snapi-field-display, .snapi-display-val, .snapi-display-titleDisplay fields (read-only {{data.x}} text)
.snapi-link-bar, .snapi-linkLink fields — rendered as a nav-style row at the top
.snapi-actionsBottom action row holding custom Buttons + the Submit button

Inputs & buttons

Class What it is
.snapi-field input, textarea, selectText/number/email/tel/date/select/textarea — one shared rule, plus a :focus variant
.snapi-radio-labelOne radio option's label row
.snapi-seq-autoA Sequence field left to auto-assign (greyed, italic, not editable)
.snapi-submitThe Submit button
.snapi-btn, .snapi-btn-primary/secondary/danger, .snapi-btn-smButton fields and table row-action buttons, by style variant and size
.snapi-field-buttonA standalone Button field's wrapper

File & reference fields

Class What it is
.snapi-file-zoneThe drag-and-drop file dropzone
.snapi-drag-over, .snapi-file-selectedDropzone states — a file is being dragged over it / one's chosen
.snapi-file-iconThe icon inside the dropzone
.snapi-ref-wrap, .snapi-ref-searchA Reference field's search input
.snapi-ref-dropdown, .snapi-ref-openIts results dropdown, and the open-state modifier
.snapi-ref-option, .snapi-ref-emptyOne result row, and the "no matches" row

Tables & charts

Class What it is
.snapi-table-scroll, .snapi-tableA Table field — horizontal-scroll wrapper and the table itself
.snapi-table-actionsA row's action-button cell
.snapi-table-empty"No rows" state
.snapi-chart-wrapA Chart field's canvas container

Messages, states & the auth modal

Class What it is
.snapi-loading, .snapi-spinnerInitial load state
.snapi-error, .snapi-btn-retryPage failed to load, with its Try Again button
.snapi-success, .snapi-success-iconPost-submission success screen
.snapi-form-errorInline submit-failure message
.snapi-user-bar, .snapi-user-logout"Signed in as…" bar on auth-required pages
.snapi-login-prompt, .snapi-login-formThe sign-in screen shown before an auth-required page
.snapi-modal, .snapi-modal-open, .snapi-modal-backdrop, .snapi-modal-box, .snapi-modal-close, .snapi-modal-titleThe login modal and confirm-action dialogs
.snapi-confirm-box, .snapi-confirm-msg, .snapi-confirm-actionsA button/row action's confirmation prompt
.snapi-toast, .snapi-toast-in, .snapi-toast-success/error/infoBottom-right toast notifications
.snapi-poweredThe "Powered by Snapi" footer
i
These class names aren't part of a versioned public API — they're stable in practice, but a future snapi.js update could rename or restructure one. If you're overriding more than colors, keep your CSS scoped (e.g. under a wrapper id) so a future change fails loudly instead of silently.

Submission limits

Public page submissions are unlimited on every plan, including Free — no monthly quota, no record cap. See Plans & limits for the full comparison.

Spam and rate limiting

The public submit endpoint is IP-rate-limited (60 requests per 15 minutes per IP) regardless of plan. For high-volume use cases, authenticated API access via an API key is recommended instead.

Receiving submissions in real time

Every submission triggers your page's configured webhooks (Builder plan or higher). You can use a post-submit webhook to send confirmation emails, sync to a CRM, or trigger automation in n8n.

i
The visitor who submits never sees what happens next. Snapi's board/kanban view is internal — for your signed-in team only, never for public submitters (see Kanban Boards). There's no built-in page where a submitter can check whether their request was approved, what stage it's in, etc. If you need to close that loop, do it explicitly: a post-submit webhook that emails a confirmation or reference number, or your own status page built on the API. Plan for this up front rather than discovering it after launch.
!
CORS: The public page endpoints accept requests from any origin by design — that's what makes embedding work. Use Allowed Domains (above) to restrict which sites can use your page.