No-Code SDK
A lightweight IIFE script hosted at cdn.swake.io/sdk.js — paste one snippet in your HTML head and start identifying users and intercepting portal links with zero npm, zero bundler, zero framework required.
Overview
The No-Code SDK is a self-contained JavaScript snippet you drop into any HTML page. It loads asynchronously from Swake's CDN at roughly 1.9 KB gzipped and makes no assumptions about your tech stack — it works in plain HTML, server-rendered apps, CMS platforms, and marketing sites alike.
Swake() made before the script loads are queued and replayed automatically.Swake('identify', …) upserts the user in Swake and registers a delegated click handler.No UI is rendered by this SDK. It does not display a feedback form, floating button, or any in-app widget. If you need those, use the @swake/react-native or @swake/web SDK integration instead.
Installation
Two steps — paste both snippets inside your HTML <head> tag, in order.
- 1Copy the loader snippetThis snippet installs a command-queue stub named
Swakesynchronously, then loads the full SDK asynchronously after the page has loaded.html<script>!function(w,d,i,s){function l(){if(!d.getElementById(i)){var f=d.getElementsByTagName(s)[0],e=d.createElement(s);e.type="text/javascript",e.async=!0,e.id=i,e.src="https://cdn.swake.io/sdk.js",f.parentNode.insertBefore(e,f)}}if("function"!=typeof w.Swake){var c=function(){c.q.push(Array.prototype.slice.call(arguments))};c.q=[],w.Swake=c,"complete"===d.readyState?l():w.attachEvent?w.attachEvent("onload",l):w.addEventListener("load",l,!1)}}(window,document,"swake-jssdk","script");</script> - 2Add the identify call immediately afterReplace the placeholder values with your project's API key and the authenticated user's details. This call is queued if the SDK hasn't finished loading yet — nothing is dropped.html
<script> Swake('identify', { appID: 'ep_live_your_api_key', user: { id: 'user_123', email: 'jane@acme.com', name: 'Jane Doe', }, }); </script>
Order-independent calls: it is always safe to call Swake() before sdk.js finishes loading. The command queue holds every call and replays it once the script is ready.
Configuration
Swake('configure', options) is optional and can be called before or after identify. The most important option is portalUrl — set it once and data-swake-link anchors no longer need an href.
// Optional — call once, before or after identify().
Swake('configure', {
portalUrl: 'https://quizly.swake.io', // your project's portal URL
// apiBase: 'https://api.swake.io', // override API URL (testing / self-hosted)
});
| Option | Default | Description |
|---|---|---|
portalUrl | — | The public portal URL for this project (e.g. https://quizly.swake.io or https://feedback.quizly.com). When set, data-swake-link anchors that have no href or href="#" use this URL automatically. |
apiBase | https://api.swake.io | Override the API base URL. Useful for local development or self-hosted deployments. |
Project-level portals: each project now has its own portal subdomain or custom domain. Set portalUrl to your project's URL (found in Project → Settings → Domains) so you don't need to hardcode it on every anchor — and so changing your portal domain later only requires one config update.
Identifying Users
Swake('identify', payload) is the single entry point for connecting your authenticated user to Swake.
interface IdentifyPayload {
appID: string; // Required — your project API key (ep_live_...)
user: {
id: string; // Required — your internal user identifier
email?: string; // Strongly recommended — shown in the portal, used for notifications
name?: string; // Display name shown in the portal
avatarURL?: string; // Sent as device metadata — not persisted (see note below)
created?: string; // Sent as device metadata — not persisted (see note below)
[key: string]: unknown; // Any custom traits (plan, company, role, etc.)
};
}
What happens internally
- 1
appIDand the user object are stored in memory for use by subsequent SDK calls. - 2A
POST /v1/sdk/identifyrequest is sent to the Swake API, upserting the user record (email/name preserved if omitted; custom traits fully replaced). - 3A delegated click listener is registered on
documentfor<a>elements carrying thedata-swake-linkattribute (the selector isa[data-swake-link]— non-anchor elements are never intercepted). - 4A success log is emitted:
[Swake] Identified user: jane@acme.com
Field reference
| Field | Required | Description |
|---|---|---|
appID | Required | Your project API key (ep_live_…). Found in Project → Settings → API Keys. |
user.id | Required | Your internal user identifier. Must be stable — changing it creates a new user in Swake. |
user.email | Strongly recommended | Email address. Shown in the Swake portal and used for reply notifications. Not used to match the user — matching is always on user.id. |
user.name | Optional | Display name shown in the Swake portal user list. |
user.avatarURL | Optional | Sent to the API as device metadata. Not currently persisted — the portal avatar comes from the server-side image field instead (see note below). |
user.created | Optional | Sent to the API as device metadata alongside avatarURL. Not currently persisted. |
user.* | Optional | Any additional custom traits — plan, company, role, etc. Stored as customMetadata. |
Avatars are set server-side, not by this SDK. avatarURL and created are sent inside the identify request's deviceMeta object, which POST /v1/sdk/identify does not currently read — so they are dropped. To show a real profile picture in the portal, pass a top-level image (a publicly reachable URL) when you call /v1/sdk/identify or /v1/sdk/portal-token from your own server. That is how the Portal Login flow supplies avatars.
Portal Links
Add data-swake-link to any anchor that should open your project's portal. The SDK intercepts the click, generates a single-use session token, and opens the portal in a new tab with the user already logged in.
Pattern 1 — href on each anchor
The original approach. Every anchor carries the full portal URL.
<!-- Pattern 1: href on every anchor (original approach) -->
<a href="https://quizly.swake.io" data-swake-link>Give feedback</a>
Pattern 2 — configure once, no href required
Set portalUrl via configure and all data-swake-link anchors without an href will use it automatically. Changing your portal domain later only requires updating one line.
<!-- Pattern 2: configure once, no href required on anchors -->
<script>
Swake('configure', { portalUrl: 'https://quizly.swake.io' });
</script>
<a data-swake-link>Give feedback</a>
<a data-swake-link>View roadmap</a>
Pattern 3 — portalUrl as default, per-anchor overrides
Anchors with an explicit href use their own URL (useful for deep-linking to a specific portal page). Anchors without href fall back to portalUrl.
<!-- Pattern 3: portalUrl as default, explicit href for specific portal pages -->
<script>
Swake('configure', { portalUrl: 'https://quizly.swake.io' });
</script>
<a data-swake-link>Give feedback</a>
<a href="https://quizly.swake.io/roadmap" data-swake-link>View roadmap</a>
<a href="https://quizly.swake.io/changelog" data-swake-link>Changelog</a>
What happens on click
- 1The SDK resolves the target URL: anchor
hreftakes precedence; if absent or#, the configuredportalUrlis used. If neither is set, a console warning is emitted and no navigation occurs. - 2Default navigation is prevented and the link cursor changes to
wait. - 3The SDK calls
POST /v1/sdk/portal-tokenwith the identified user's ID and your API key (3-second timeout). - 4A single-use token valid for 5 minutes is returned.
- 5The resolved URL receives
?swt=TOKENand the portal opens in a new tab. - 6The portal validates the token, creates a session cookie, strips the
?swt=param, and the user arrives authenticated.
Fallback behavior: if the token request fails or times out, the SDK opens the resolved portal URL without a token. The user lands on the portal as an anonymous visitor.
The user must already exist in Swake. POST /v1/sdk/portal-token looks the user up by the user.id you passed to identify and returns 404 NOT_FOUND if there is no match — which the SDK treats as a failed token request and falls back to anonymous navigation. Since identify fires the upsert without blocking, a click within the same instant as the very first identify call can lose that race; a server-side integration should call /v1/sdk/identify and await it before minting a token.
The reverse direction — Portal Login. This SDK covers your app → the portal, for a user who is already signed in with you. The opposite flow — an anonymous portal visitor clicking Login and authenticating in your app — is a separate, per-project setting configured under Project → Settings → General. Swake sends that visitor to your login page with ?from_swake=true&redirect_uri=…; your server then mints the same ?swt= token via POST /v1/sdk/portal-token and redirects back. Both directions share one token mechanism, but the Portal Login flow never puts your API key in the browser.
Shareable Surveys
Every survey has a public shareable URL that works without any SDK integration on the respondent's device — ideal for email campaigns, QR codes, and in-app banners.
| Domain setup | Survey URL format |
|---|---|
| Project subdomain | https://{projectSlug}.swake.io/s/{surveySlug} |
| Custom domain | https://feedback.acme.com/s/{surveySlug} |
Survey slugs are auto-generated (6-character alphanumeric) when a survey is created. Find the shareable link in Dashboard → Installation → No-code tab. The shareable flag is on by default for every new survey; turning it off makes the public page return 404.
Use cases
Troubleshooting
| Symptom | Check |
|---|---|
| No [Swake] logs in the console | Verify the loader snippet is in the <head> of your page. Check the Network tab for a request to cdn.swake.io/sdk.js. |
| Identify API call failed | Make sure appID and user.id are both present in the identify payload. Both fields are required. |
| Portal link not intercepted | Confirm the element is an <a> tag with the data-swake-link attribute. identify() must have been called before the click. |
| Click does nothing — "[Swake] data-swake-link: no href…" in console | The anchor has no href and no portalUrl is configured. Either add href="https://your-project.swake.io" to the anchor, or call Swake('configure', { portalUrl: 'https://your-project.swake.io' }) once on the page. |
| CSP error: refused to load cdn.swake.io | Add cdn.swake.io to your Content-Security-Policy script-src directive: script-src 'self' cdn.swake.io |
Still stuck? Open a support ticket from the portal or email support@swake.io with your workspace slug and a description of the issue. Include any relevant console errors and Network tab screenshots.