One script. Async-loaded. About 9kB gzipped. Templates ship with this already wired.
The snippet
html
<script
src="https://pinpoints.caddi.build/v1.js"
data-caddi-project="proj_01H9X4..."
async
></script>
You can get the data-caddi-project value from the project page in the dashboard or with caddi project info --json.
Next.js (app router)
tsx
// app/layout.tsx
import Script from 'next/script';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html>
<body>
{children}
{process.env.NEXT_PUBLIC_CADDI_PINPOINTS === 'on' && (
<Script
src="https://pinpoints.caddi.build/v1.js"
data-caddi-project={process.env.NEXT_PUBLIC_CADDI_PROJECT_ID}
strategy="afterInteractive"
/>
)}
</body>
</html>
);
}
Astro
astro
---
const ON = import.meta.env.PUBLIC_CADDI_PINPOINTS === 'on';
const PROJECT = import.meta.env.PUBLIC_CADDI_PROJECT_ID;
---
<html>
<body>
<slot />
{ON && (
<script
is:inline
src="https://pinpoints.caddi.build/v1.js"
data-caddi-project={PROJECT}
async
/>
)}
</body>
</html>
We strongly recommend gating the script on an env var so production stays clean. See
Per-env toggle.
Triggering
- Default keybind:
Cmd+Shift+P (or Ctrl+Shift+P on Windows/Linux). - Or call
window.Caddi.pinpoints.open() from your own button.
What clients see
Nothing, until they hit the keybind. Then a slim floating bar appears in the top-right asking them to click an element and write a note. Submit closes it. No FAB, no widget intrusion.
Next
Per-env toggle →