HTML and site builders

Add the chat widget to a plain HTML site, Webflow, Framer or WordPress with one script tag.

One script tag works on any site. Get your publishable key (pk_...) from Settings → Widgets. It’s public, so it’s fine in your HTML.

Install

html
<script src="https://widget.ticketping.com/v2/loader.js" data-key="pk_..." async></script>

Where to paste it:

SiteWhere
Plain HTMLBefore </body> on every page, or in your shared layout
WebflowSite settings → Custom code → Footer code. Publish the site
FramerSite settings → General → Custom code → End of <body> tag. Publish the site
WordPressYour theme’s footer, or a plugin that adds code to the footer (for example WPCode). Avoid page builders that strip data- attributes

Open the site on localhost (or your published URL) and click the launcher. Messages from localhost are marked as test conversations, so you can try everything safely.

Identify signed-in users

Marketing sites usually stay anonymous, and that’s fine: visitors can still chat and leave an email for replies.

If your site has logins, render the signed-in user’s details into the page and pass a getToken function. The widget calls it to get a short-lived token from your server, which proves the user is who the page says.

html
<script>
  window.Ticketping ||= (...args) => (Ticketping.q ||= []).push(args)

  Ticketping('identify', {
    userId: 'u_123',
    email: 'ada@acme.com',
    name: 'Ada Lovelace',
    getToken: async () => {
      const res = await fetch('/api/ticketping-token', { method: 'POST' })
      if (!res.ok) throw new Error(`Ticketping token request failed (${res.status})`)
      return res.text()
    }
  })
</script>

When the user signs out, call Ticketping('logout'). The widget forgets them and starts a new anonymous visitor, so the next person on the device can’t see their conversations.

Add the token route

/api/ticketping-token is the one piece of server code you write. It checks who’s signed in, signs { sub, email, name, exp } with TICKETPING_IDENTITY_SECRET, and returns the token as text. Copy it for your backend:

Get the secret from Settings → API → Identity secret and set it as an environment variable on your server:

bash
TICKETPING_IDENTITY_SECRET=tpis_...

The identity secret signs tokens for any user. Keep it on your server, out of your HTML and out of version control.

Check that it works

  1. Sign in to your site and open the browser console.
  2. Run await (await fetch('/api/ticketping-token', { method: 'POST' })).text() and copy the token.
  3. Paste it into the token validator under Settings → API → Identity secret. It shows the claims, or says exactly what to fix.
  4. Reload the page and send a message. In the dashboard, the conversation shows the user’s name with a Verified badge.

The validator never marks a token as used, so checking a token there doesn’t break the widget.

Options

Pass options with an explicit init instead of data-key when you need them. Code settings override the dashboard:

html
<script>
  window.Ticketping ||= (...args) => (Ticketping.q ||= []).push(args)
  Ticketping('init', {
    publishableKey: 'pk_...',
    appearance: { accentColor: '#0F766E', position: 'bottom-left' },
    texts: { greetingTitle: 'Questions? Ask us.' }
  })
</script>
<script src="https://widget.ticketping.com/v2/loader.js" async></script>

See Configuration for every setting and the JavaScript API for methods like open, showNewMessage and events.

Copy prompt for your AI coding agent

prompt
Add the Ticketping chat widget (v2) to this site.

Docs index: https://ticketping.com/llms.txt
This page as Markdown: https://ticketping.com/docs/vanilla-js.md

1. Add this to the shared layout, before </body>, so it's on every page:
   <script src="https://widget.ticketping.com/v2/loader.js" data-key="pk_..." async></script>
   Ask me for the publishable key (starts with pk_). It's public and the same in every environment.
2. If the site has signed-in users:
   a. Add a POST /api/ticketping-token route on the server, using the code for this backend from
      https://ticketping.com/docs/authentication.md. It must return 401 when nobody is signed in, and
      otherwise return, as plain text, an HS256 JWT signed with the TICKETPING_IDENTITY_SECRET env var:
      { sub: String(user id), email, name, exp: now + 300 seconds }. Leave out email or name if empty.
   b. Add TICKETPING_IDENTITY_SECRET=tpis_... to the server's env file (not committed) and to .env.example without a value.
   c. In the layout, for signed-in users only, add:
      window.Ticketping ||= (...args) => (Ticketping.q ||= []).push(args)
      Ticketping('identify', { userId, email, name, getToken }) where getToken POSTs to /api/ticketping-token and returns the response text (throw if the response isn't ok).
   d. Call Ticketping('logout') when the user signs out.
3. Never cache tokens and never expose the secret to the browser.
4. Tell me how to verify: sign in, run await (await fetch('/api/ticketping-token', { method: 'POST' })).text() in the console, and paste the token into the token validator at Settings → API → Identity secret.