CDN / Script Tag
Integration guide for CDN / Script Tag. Uses the @swake/web SDK, which works with any JavaScript framework or vanilla JS.
Overview
The CDN build exposes the exact same @swake/web API as a global Swake object — no bundler or npm install required.
@swake/web/ui (the trigger button, feedback form, history panel, and survey modals) is a separate <script type="module"> import — load it alongside the core script to get the visual widgets.
Getting Started
Script Tag (IIFE)
Load Swake via a script tag with no bundler required. The IIFE bundle exposes a global Swake object.
<head>
<script src="https://cdn.jsdelivr.net/npm/@swake/web/dist/swake.min.global.js"></script>
</head>
<body>
<script>
Swake.default.init({ apiKey: 'ep_live_your_api_key' });
Swake.default.identify({
userId: 'user_123',
email: 'user@example.com',
});
document.getElementById('feedback-btn').addEventListener('click', () => {
Swake.default.showFeedbackForm();
});
</script>
</body>
The IIFE bundle does not include the UI widgets. For the FAB and widget panels via CDN, add: <script type="module">import '@swake/web/ui';</script>
ESM via CDN
<script type="module">
import Swake from 'https://cdn.jsdelivr.net/npm/@swake/web/dist/index.js';
import 'https://cdn.jsdelivr.net/npm/@swake/web/dist/ui.js';
Swake.init({ apiKey: 'ep_live_your_api_key' });
</script>
Identifying Users
Swake.default.identify({
userId: 'user_123',
email: 'jane@example.com',
name: 'Jane Smith',
});
// On logout
Swake.default.clearIdentity();
Customization
Theme Customization
Pass a theme object in init() to brand all Swake widgets.
<script type="module">
import Swake from 'https://cdn.jsdelivr.net/npm/@swake/web/dist/index.js';
import 'https://cdn.jsdelivr.net/npm/@swake/web/dist/ui.js';
Swake.init({
apiKey: 'ep_live_your_api_key',
theme: {
primaryColor: '#8B5CF6',
backgroundColor: '#FFFFFF',
textColor: '#0F172A',
borderRadius: 12,
},
});
</script>
| Token | Default | Description |
|---|---|---|
primaryColor | #6366F1 | Buttons, active states, FAB background |
successColor | #10B981 | Success checkmark, resolved badges |
dangerColor | #EF4444 | Error borders, destructive actions |
backgroundColor | #FFFFFF | Modal / panel background |
surfaceColor | #F9FAFB | Input fields, secondary surfaces |
overlayColor | rgba(0,0,0,0.45) | Backdrop behind modals |
textColor | #111827 | Primary text |
textSecondary | #6B7280 | Labels, placeholders, metadata |
borderColor | #E5E7EB | Input borders, dividers |
borderRadius | 8 | Corner radius for inputs and buttons |
fontFamily | System default | Font family applied to all text |
AI Setup
Using Claude Code
Prefer to let an AI coding agent do the wiring? Run Claude Code from the root of your site's repository and paste the prompt below. It reads this page as the source of truth, detects your stack, asks you for your API key and anything else it needs, then wires up @swake/web, init(), identify(), the widgets, and theming — in one shot.
Prerequisites
Before you run the prompt, set up your project in the Swake portal and grab the values the agent will ask for — it only writes placeholders to your env files, so you supply the real values yourself.
- Sign in to the Swake portal and create a project (or open an existing one).
- In Project settings → API keys, get existing or create a key and copy it — it's shown once and starts with
ep_live_. - If you want the voting board, create a board and copy its slug from the board's URL / settings.
Have these ready when the agent prompts you — the same values you'd otherwise wire up by hand:
| Value | Where to find it | Example |
|---|---|---|
apiKey | Project settings → API keys | ep_live_… |
baseUrl | API endpoint — optional, only needed when self-hosting or routing through your own backend | https://api.swake.io |
boardSlug | Voting board settings — optional; passed to getBoardItems() / vote(), not init() | public-roadmap |
The prompt is additive and safe: a Swake or network error must never break your app's boot or existing behavior. The agent pauses and asks before installing packages or writing secrets, and it never guesses APIs that aren't in these docs. Note that a CDN integration necessarily exposes your ep_live_ key in page source, and the browser asserts the user's identity. The prompt states this outright and offers the server-side alternative if that trade-off doesn't suit you.
The prompt
Run Claude Code from your project root and paste this. It targets CDN / Script Tag specifically, and re-reads this guide before it writes anything, so it stays correct as the SDK evolves.
Fully integrate the Swake feedback SDK (feedback forms, bug reports, surveys/NPS, in-app notifications, voting boards) into this site via the Swake CDN build, with no bundler. Treat it as additive: a Swake or network failure must NEVER break boot or any existing behavior. Never invent or fake an API that is not in the docs.
1. READ THE DOCS FIRST — they are the source of truth for the script URLs, the global shape, init/identify and every option. Fetch with a browser User-Agent (a plain fetch returns 403): curl -sL -A "Mozilla/5.0" "https://swake.io/docs/sdk?framework=cdn". Then find the pages or templates this needs to go into (a shared header, layout, or template partial — not one page at a time) and say which files you intend to edit before editing them.
2. ASK BEFORE DECIDING ANYTHING (AskUserQuestion): (a) the API key (ep_live_...), an optional baseUrl, and a voting-board slug if they want the board; (b) whether they want the IIFE script tag or the ESM build. Then tell them plainly, before writing anything: in this mode the key IS visible to anyone viewing source, and because the browser asserts the user id, a determined visitor could submit as someone else. That is the accepted trade-off of a no-build integration. If either matters, offer the alternative — serve the page from a backend that holds the key, point baseUrl at that backend with credentials: 'include', and have it attach X-API-Key and force the user id from the session.
3. ADD THE SCRIPTS — the IIFE bundle exposes a global and is reached as Swake.default.*, but it does NOT include the visual widgets. To get the trigger FAB, feedback form, history panel and survey modals you must ALSO add the widget module in a separate script type="module". If they chose ESM instead, import both the core and the UI module from the CDN and use the default export directly rather than Swake.default. Put this in ONE shared template so it loads on every page exactly once.
4. INIT EXACTLY ONCE — call init() a single time from that shared template, before any other call. Never repeat it per page or per handler: every method throws "[Swake] SDK not initialized" before init(), and a second init() starts a second offline-queue flush interval without clearing the first. Set trigger: { hidden: true } only if the site will open feedback from its own button.
5. IDENTITY — if the site knows who the visitor is, call identify({ userId, email, name, metadata }) on EVERY page load, not once at login: identity is held in memory only, so each navigation starts fresh. userId must be a stable internal id and every metadata value must be a string. Call clearIdentity() on logout inside a try/catch. If visitors are anonymous, skip identify entirely — feedback still submits, but history, notifications, surveys and voting all need an identified user.
6. POLISH — if the site has a light/dark toggle, call setTheme() from it. Keep the snippet off any page where a survey would be unwelcome (checkout, sign-up), and wire your own buttons to showFeedbackForm() and showFeedbackHistory() where the design calls for it.
7. VERIFY & REPORT — check the browser console for errors on a page that loads the snippet, confirm the trigger appears and that a real submission reaches the Swake dashboard, and confirm the widgets actually render (if they do not, the widget module script is missing — the single most common mistake in this mode). Report which templates you edited, anything left manual, and every assumption you made.
Reference
Full API Reference
Click any method for its full signature, parameters, and an example.
| Method | Description |
|---|---|
| Initialise the SDK. Must be called once before any other method. | |
| Link the current user. Returns a Promise. | |
| Clear user identity and offline queue. | |
| Replace the global UI theme at runtime. | |
| Submit feedback programmatically (with optional customFields). Returns the created Submission. | |
| Fetch the project's custom field definitions for a custom feedback UI. | |
| List the current user's submissions. | |
| Get full submission detail with comments + attachments. | |
| Edit a submission you own. | |
| Unpublish a submission you own (one-way). | |
| Lightweight submission history (id, title, status, type, createdAt). | |
| Get unread notification count. | |
| List notifications with pagination. | |
| Mark a single notification as read. | |
| Mark all notifications as read. | |
| Open the built-in feedback form widget. | |
| Open the feedback history panel. | |
| Open the feedback form (alias). | |
| Close any open widget (form, history, survey). | |
| Show or hide the trigger FAB. | |
| Fetch voting board items. | |
| Cast a vote on a board item. | |
| Remove a vote from a board item. | |
| Re-check survey eligibility and show the first match. | |
| Show a specific survey (ignores cooldown). | |
| Register survey-shown event listener. | |
| Register survey-completed event listener. | |
| Register survey-dismissed event listener. | |
| Add a custom breadcrumb to the trail. | |
| Return a snapshot of the breadcrumb trail. | |
| Clear the breadcrumb ring buffer. | |
| Register plan-limit event listener. | |
| Tear down all listeners, timers, and UI. |