Skip to main content
Sleekplan’s Single Sign-On (SSO) lets users who already have accounts in your application log in to the feedback widget without a separate sign-up step. SSO is built on JSON Web Tokens (JWT), a standard for securely passing authentication data between systems. When a user performs an action that requires authentication — such as voting or submitting feedback — Sleekplan calls $sleek.sso, your server generates a signed token, and Sleekplan accepts it to authenticate the user instantly.

How it works

  1. A user performs an authenticated action (vote, submit feedback) in the widget.
  2. The JavaScript SDK calls $sleek.sso.
  3. Your server generates a JWT signed with your private SSO key.
  4. You pass the token back via the callback() parameter.
  5. Sleekplan verifies the token and logs the user in seamlessly.

1

Get your SSO secret key

Find your private SSO key in Settings → Developer inside the Sleekplan dashboard at https://app.sleekplan.com/settings/developer.
Your SSO secret key must only ever be used server-side. Never expose it in client-side JavaScript, public repositories, or environment variables that are shipped to the browser. Anyone who obtains the key can generate tokens for any user.
The SSO secret key is available on the Starter and Business plans.
2

Generate a JWT on your server

Use your language’s JWT library to create a token signed with HMAC SHA-256 (HS256). Replace PRIVATE_SSO_KEY with the key from the previous step. The localUser parameter in each example represents your application’s user record.
Install the library
Generate a token

JWT payload attributes

Sleekplan requires an email address to uniquely identify each user. All other attributes are optional but recommended.
3

Authenticate the user with the token

Once you have a token, pass it to Sleekplan using the method that fits your integration type.
Set window.SLEEK_USER before the widget snippet loads. This is the simplest approach for server-rendered pages where the user is already authenticated when the page is served.
Place the window.SLEEK_USER block before the widget snippet. If the snippet loads first, it will not pick up the token.

Companies and teams

Add a groups array to the token to mirror the accounts in your own system (a company, a team, or a workspace) so admins can see feedback, votes and satisfaction rolled up by account instead of only by individual user. Groups are created and updated automatically from this claim.
Groups require the Business plan or higher. On lower plans a groups claim is accepted and silently ignored, so nothing appears in the dashboard until the workspace upgrades. If groups aren’t showing up, check the plan first.
Only id is required on each entry. Everything else is optional and defaulted.
string
required
Your identifier for the account. This is what Sleekplan matches on across sign-ins, so treat it as immutable: changing it creates a different group rather than renaming the existing one. Up to 255 characters.
string
Display name. If omitted, the dashboard shows the identifier instead. Up to 255 characters.
string
default:"company"
A free-form label such as company, team, or workspace. Normalized on write, see Type normalization below.
integer
default:"1"
A 0 to 10 prioritization weight for the account’s votes, clamped to that range. The group equivalent of the user-level weight above.
integer
default:"0"
Monthly recurring revenue for the account, in whole units of your workspace’s currency. No minor units, no currency conversion.
object
Flat key/value custom data, string values only. Up to 20 keys per group, each key and value up to 255 characters.

Sync is additive, always

A sign-in creates and updates groups and memberships. It never removes a membership, even for a group that used to appear in the token and no longer does. Removing a customer from a group is an explicit action an admin takes in the dashboard, from the group’s Members tab or the customer’s Groups tab. This is deliberate: groups can also be populated by an external CRM sync running independently of your login flow, and a sign-in that silently pruned what another integration just wrote would destroy data no one asked it to touch.

Only the fields you actually send are touched

Resyncing an account only updates the keys present in that entry. A token that sends { id: 'acme-inc' } on every login updates nothing but the membership itself: it can never blank an mrr or a custom meta key an admin later set by hand. Sending the same entry twice in a row writes nothing the second time. There is one deliberate asymmetry in how metadata is handled:
  • SSO sync merges a meta object into the group’s existing metadata. A key an admin added by hand survives a later sign-in that doesn’t mention it.
  • An admin’s edit in the dashboard replaces the whole metadata object. The admin is looking at the full panel and saving what they see, so a key they delete there stays deleted, even though a later sync would have merged it back if the token happened to carry it.

Limits

A bad groups claim never fails the sign-in. Entries past the tenth are dropped, and a malformed entry (not an object, or missing id) is skipped on its own without discarding the rest of the array. Beyond that the two bounds behave differently, so it is worth knowing which is which:
  • An over-length id or name skips that entry entirely. It is not truncated, because a truncated identifier would silently create or match the wrong group.
  • An over-length type or meta key/value is truncated, and weight and mrr are clamped into range.

Type normalization

A type is lowercased, its whitespace becomes a hyphen, anything outside a-z, 0-9, _ and - is stripped, repeated hyphens collapse to one, and the result is capped at 64 characters. Key Account becomes key-account. An input that normalizes to nothing, or an omitted type, falls back to company. This exists so Company, company and Company from three different sessions land in the same bucket. Without it a workspace could spend its entire type budget on casing variants of the same account type and see its own numbers split across them.
Distinct types are capped at 10 per workspace. Once a workspace has 10 types in use, an entry with a genuinely new type falls back to company rather than minting an eleventh. A type that already exists is never affected by the cap. Don’t send anything high-cardinality as a type (the company name, an account id): use type for the kind of account and id for the account itself.

When groups appear

Sync runs at sign-in, and sign-in only happens when the widget has no existing session, exactly like weight and meta already work. A change on your side (a renamed account, a new team in the token) shows up the next time that user starts a fresh session, not on their next page view within an existing one. Sync is also dispatched to a background worker rather than run inline, so a sign-in never waits on it and never fails because of it. In practice a synced group or membership can take a moment to appear after the session starts.

User setup

The $sleek.setUser() method and the rest of the user payload.

Companies and teams in the dashboard

How groups look for your team once they start arriving.