Get Started

Manual install

The plain script tag, framework recipes, and CSP notes for engineers who want to wire it up by hand.

Plain HTML

Drop this near the closing </body>:

html
<script
  src="https://duggai.com/embed.js"
  data-key="duggai_pk_XXXXXXXXXXXX"
  defer
></script>

Next.js (App Router)

Use the Script component from next/script in your root layout:

tsx
// src/app/layout.tsx
import Script from "next/script";

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        {children}
        <Script
          src="https://duggai.com/embed.js"
          data-key={process.env.NEXT_PUBLIC_DUGGAI_KEY}
          strategy="afterInteractive"
        />
      </body>
    </html>
  );
}

Next.js (Pages Router)

tsx
// pages/_app.tsx
import Script from "next/script";

export default function App({ Component, pageProps }) {
  return (
    <>
      <Component {...pageProps} />
      <Script
        src="https://duggai.com/embed.js"
        data-key={process.env.NEXT_PUBLIC_DUGGAI_KEY}
        strategy="afterInteractive"
      />
    </>
  );
}

React (Vite, CRA, Remix)

Add the script in your index.htmllike the plain HTML example. Don't load it inside a React component — the widget self-mounts to the DOM and React doesn't need to know about it.

Vue / Nuxt

ts
// nuxt.config.ts
export default defineNuxtConfig({
  app: {
    head: {
      script: [
        {
          src: "https://duggai.com/embed.js",
          "data-key": process.env.DUGGAI_KEY,
          defer: true,
        },
      ],
    },
  },
});

Svelte / SvelteKit

Add to app.html:

html
<!-- src/app.html -->
<body>
  %sveltekit.body%
  <script
    src="https://duggai.com/embed.js"
    data-key="duggai_pk_XXXXXXXXXXXX"
    defer
  ></script>
</body>

Content Security Policy

If you set a CSP, allow the widget's origin in script-src, connect-src, and img-src:

http
Content-Security-Policy:
  script-src 'self' https://duggai.com;
  connect-src 'self' https://duggai.com;
  img-src 'self' data: https://duggai.com;

Single-page apps

The widget watches for route changes via the History API and updates the page-context signal it sends to the agent. No extra wiring needed — but if you call history.pushState manually for analytics, the widget will pick that up too.

Identifying logged-in users
The data-key attribute is your widget public key (prefix duggai_pk_) and is safe to ship in client code. To prove a user is who they claim to be (so the agent can read their account context), call DuggAI.identify({ id, hash }) where hashis an HMAC-SHA256 of the user id computed on your server using your widget's webhook secret. Never expose the webhook secret in client code.