Skip to content

Embedding a Contact form

A Contact form puts a form on your own website. Each submission opens a Ticket in the desk. An admin creates the form in Settings → Contact forms and copies its snippet from there.

The snippet

<iframe
src="https://help.example.com/form/<id>#theme=light"
title="Contact form"
height="600"
data-kiku-form
style="border:0;width:100%"
></iframe>
<script src="https://help.example.com/embed.js" async></script>

help.example.com stands for your APP_URL. Paste both lines where the form should appear. Kiku serves embed.js, so fixes to it reach your site without you editing anything.

A browser only shows the form on a site listed in the form’s allowed domains. A form with no allowed domains opens only at its own URL, which is how you test it before embedding it.

embed.js sets the iframe’s height to the form’s content, so the frame grows with error messages and shrinks to the confirmation after sending. It is optional. If your site blocks it, the form still works at the height in the snippet, so pick a height that fits the form.

Theme

The form’s background is transparent, so it takes the color of your page. Tell it which text colors to use with #theme= in the iframe’s src:

ValueForm
lightDark text, for a light page. The default.
darkLight text, for a dark page.
systemFollows the visitor’s operating system setting.

A missing or unknown value is light.

embed.js also sets the iframe’s color-scheme to match. Without that, a browser paints an opaque backdrop behind a frame whose color scheme differs from the page’s. If your site has its own light/dark toggle, update both when it flips, with theme as light or dark:

frame.src = frame.src.replace(/theme=\w+/u, `theme=${theme}`)
frame.style.colorScheme = theme

Changing only the hash switches the theme in place, so the visitor keeps what they typed.

Prefilling the email

If your site knows the visitor’s email address, add it to the hash:

<iframe src="https://help.example.com/form/<id>#theme=light&email=ada%40example.com" ...></iframe>

The form puts it in the email field, where the visitor can still change it. It reads #email= only inside an iframe. A link that someone opens directly ignores it. Besides #theme and #email, the form reads nothing from the URL.

Sites with a strict Content Security Policy

If your site sends a Content-Security-Policy header, add your APP_URL origin to two directives:

frame-src https://help.example.com;
script-src https://help.example.com;

Without frame-src the form does not load. Without script-src the form loads but keeps the snippet’s fixed height.

Two iframe attributes break the form:

  • sandbox without allow-same-origin. The form can no longer call its own API.
  • Chrome’s csp attribute. Kiku does not send the Allow-CSP-From header that opts a page into it, so Chrome can refuse to load the frame.

Who can open Tickets

Anyone who has seen a form’s snippet can open Tickets in its organization while the form is enabled. The id in the URL is not a secret: it is in the HTML of every page that embeds the form. The allowed domains are not a limit either. They stop other sites from framing the form in a visitor’s browser, but a script can post to the form’s API from anywhere.

To stop a form from taking submissions, turn it off or delete it in Settings.

Do not add a secret key or an Origin check in front of the form. A key in the snippet is public the moment the page loads, and any client other than a browser can send whatever Origin it likes. Both would add configuration and protect nothing.