> ## Documentation Index
> Fetch the complete documentation index at: https://sleekplan.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Troubleshooting

> Diagnose and fix the most common Sleekplan integration issues — widget not appearing, SSO failures, popup cool-downs, webhook delivery, and Canvas verification

This page covers the most common issues customers run into when integrating Sleekplan. Open the browser developer tools (F12) and watch the Console tab when working through any step.

## JavaScript SDK

<AccordionGroup>
  <Accordion title="Blank page when using `$sleek.open()` or `$sleek.showPopup()`">
    Firefox requires that `$sleek.open()` and `$sleek.showPopup()` be triggered directly from a user interaction event. Calling them from the console or outside an event handler causes a blank widget page.

    <Steps>
      <Step title="Attach the call to an onclick event">
        Wrap the method in a button click handler so Firefox allows the call:

        ```html button with onclick handler theme={"system"}
        <button onclick="$sleek.open()">Open feedback</button>
        ```

        Or use an event listener in your JavaScript:

        ```javascript event listener approach theme={"system"}
        document.getElementById('feedback-btn').addEventListener('click', function () {
          $sleek.open();
        });
        ```
      </Step>

      <Step title="Test in Chrome or Safari">
        If you need to test quickly via the browser console, switch to Chrome or Safari. Neither browser applies the same restriction as Firefox.
      </Step>

      <Step title="Check the browser console for errors">
        Open developer tools and check the Console tab. Any error messages shown there will help pinpoint the root cause.
      </Step>
    </Steps>
  </Accordion>

  <Accordion title="The feedback button is not displayed">
    <Warning>
      If your board is set to **private**, the feedback button will not appear until a user is authenticated via single sign-on. Set up SSO first, then revisit this section if the button is still missing.
    </Warning>

    If your board is public and the button is still not showing, check each setting below in order:

    <Steps>
      <Step title="Confirm the button is enabled">
        Go to **Settings → Widgets → Settings** and open the **Button position** tab. Make sure **Show button** is turned on.
      </Step>

      <Step title="Check display conditions">
        Open the **Display conditions** tab. Confirm that **Display for user** and **Display for visitor** are both enabled, not disabled.
      </Step>

      <Step title="Review URL conditions">
        Still on the **Display conditions** tab, inspect the **INCLUDE URL** and **EXCLUDE URL** fields. A URL rule may be suppressing the button on your page. Remove all URL conditions temporarily to confirm whether a rule is the cause.
      </Step>
    </Steps>
  </Accordion>

  <Accordion title="Changelog popup not displaying after publishing an announcement">
    If you published a changelog update with in-app announcements enabled but the popup never appears, work through the checks below in order.

    <Steps>
      <Step title="Reset the cool-down period">
        Each announcement is shown to a user only once. Consecutive announcements have a **1-hour cool-down**. When testing, find the `_sleek_session` cookie in your browser's storage tools and delete it, then reload the page.
      </Step>

      <Step title="Verify SSO for private boards">
        If your board is private, single sign-on must be working correctly. A broken SSO prevents the popup from appearing. Follow the SSO troubleshooting steps in the section below to confirm authentication is working.
      </Step>

      <Step title="Account for segmentation delay">
        Announcements that use user segmentation can take **up to 30 minutes** to start delivering. Wait and check again after that window has passed.
      </Step>

      <Step title="Confirm announcement settings">
        Open the changelog post and verify that:

        * The post status is **Published** (not Draft or Scheduled).
        * **In-app announcement** is enabled on that specific post.
      </Step>

      <Step title="Check the browser console">
        Open developer tools and look for errors or warnings related to the announcement popup in the Console tab.
      </Step>
    </Steps>
  </Accordion>

  <Accordion title="Error: `$sleek already exist`, make sure you do not load the `$sleek` SDK twice">
    This error means the SDK script tag appears more than once on your page. Search your HTML, bundler output, and any tag manager configurations for duplicate Sleekplan script inclusions and remove all but one.

    Your page should contain the Sleekplan snippet exactly once, placed before the closing `</head>` tag:

    ```html theme={"system"}
    <!-- Include the Sleekplan SDK only once -->
    <script type="text/javascript">window.$sleek=[];window.SLEEK_PRODUCT_ID=XY;(function(){d=document;s=d.createElement("script");s.src="https://client.sleekplan.com/sdk/e.js";s.async=1;d.getElementsByTagName("head")[0].appendChild(s);})();</script>
    ```
  </Accordion>

  <Accordion title="Widget opens but doesn't authenticate the user">
    For SSO-enabled or private boards, the widget requires a valid JWT token. Provide the token through one of these methods:

    ```javascript via SLEEK_USER global theme={"system"}
    window.SLEEK_USER = {
      token: 'your-signed-jwt-token'
    };
    ```

    ```javascript via $sleek.setUser() theme={"system"}
    $sleek.setUser({
      mail: 'user@example.com',
      token: 'your-signed-jwt-token'
    });
    ```

    If the token is present but authentication still fails, see the [SSO troubleshooting](/authentication/single-sign-on) section below.
  </Accordion>

  <Accordion title="No cookies or localStorage available">
    In highly restrictive browser environments, both cookies and `localStorage` may be blocked. The SDK falls back to an in-memory store, which means the session will not persist across page reloads. There is no workaround for environments that block all storage — you will need to call `$sleek.setUser()` on every page load to re-establish the session.
  </Accordion>

  <Accordion title="CSP or ad blocker is blocking the widget">
    If your Content Security Policy (CSP) or a browser extension is blocking the widget, add the following to your allowlist:

    * The Sleekplan SDK script source
    * `https://api-client.sleekplan.com/`

    Example CSP directive additions:

    ```text theme={"system"}
    script-src 'self' https://client.sleekplan.com;
    connect-src 'self' https://api-client.sleekplan.com;
    frame-src 'self' https://embed-*.sleekplan.app;
    ```

    Instruct users who report the widget not loading to check whether a browser extension such as uBlock Origin or Privacy Badger is active, and to add an exception for your domain.
  </Accordion>
</AccordionGroup>

## Single sign-on

<AccordionGroup>
  <Accordion title="User is not logged in despite the SSO token being set">
    Work through the following checks to identify where the SSO flow is breaking down.

    <Steps>
      <Step title="Confirm the JWT secret key">
        Go to **Settings → Developer**. Verify that the secret key you are using to sign your JWT matches the one shown there. If you accidentally rotated the key at any point, your existing tokens will be invalid — regenerate them using the current key.
      </Step>

      <Step title="Validate the token at jwt.io">
        Paste your generated token into [jwt.io](https://jwt.io) and confirm that:

        * The signature verifies successfully against your secret key.
        * The payload contains the correct fields in the correct format (`id`, `mail`, `name`, etc.).
        * The token has not expired.
      </Step>

      <Step title="Check the browser console">
        Open developer tools and look for SSO-related error messages in the Console tab.
      </Step>

      <Step title="Inspect the _sleek_product cookie">
        After the page loads, open your browser's storage inspector and look for a cookie named `_sleek_product`. It must contain a `user_data` object. If the cookie is missing entirely, or exists but lacks `user_data`, SSO is failing before it completes.
      </Step>
    </Steps>
  </Accordion>

  <Accordion title="Feedback button not shown on private board even with SSO configured">
    On private boards, the feedback button only appears **after** successful SSO authentication. If SSO is working but the button is still not visible, check these settings:

    <Steps>
      <Step title="Enable the button">
        Go to **Settings → Widgets → Settings → Button position** tab. Confirm **Show button** is enabled.
      </Step>

      <Step title="Check user display conditions">
        Open the **Display conditions** tab and confirm that **Display for user** is not disabled. On private boards, visitors cannot see the button — only authenticated users can.
      </Step>

      <Step title="Review URL conditions">
        On the same tab, inspect the **INCLUDE URL** and **EXCLUDE URL** fields. Remove all URL conditions temporarily to rule out a URL rule hiding the button.
      </Step>
    </Steps>
  </Accordion>
</AccordionGroup>

## Webhooks

<AccordionGroup>
  <Accordion title="My endpoint never receives any events">
    <Steps>
      <Step title="Check the webhook registration">
        Confirm the webhook is saved with a publicly reachable HTTPS URL in **Settings → Developer**.
      </Step>

      <Step title="Verify your endpoint accepts POST and returns 2xx">
        Confirm your endpoint accepts POST requests and responds with a 2xx status code. Any other response code causes Sleekplan to treat the delivery as failed.
      </Step>

      <Step title="Check your server logs">
        Check your server logs for the matching POST timestamp to confirm whether the request reached your server at all.
      </Step>

      <Step title="Review firewall and middleware rules">
        Confirm your firewall or middleware is not rejecting requests by source IP. Outbound webhook calls originate from `168.119.102.99`.
      </Step>
    </Steps>
  </Accordion>

  <Accordion title="I get events but the secret check is failing">
    <Steps>
      <Step title="Check the URL stored in Sleekplan">
        You append the secret as `?key=...` to your endpoint URL when registering the webhook. Check the URL stored in **Settings → Developer** to confirm the `key` parameter is present and correct.
      </Step>

      <Step title="Read the key query parameter on your server">
        Confirm your server reads the `key` query parameter — not a header. Sleekplan does not currently sign webhook payloads with a header-based signature; the secret is passed as a URL query parameter only.
      </Step>
    </Steps>
  </Accordion>

  <Accordion title="I subscribed to events but only some come through">
    <Steps>
      <Step title="Verify event name spelling">
        Event names use the `category.action` format. Re-check spelling against [Webhooks → Events](/webhooks#events).
      </Step>

      <Step title="Check for delete-action subscriptions">
        Events for deleted resources only fire if you subscribed to the matching `.delete` action. Confirm your subscription includes the correct action suffix.
      </Step>
    </Steps>
  </Accordion>
</AccordionGroup>

## Canvas (Custom Integration)

<AccordionGroup>
  <Accordion title="My Canvas section is empty in the admin">
    <Steps>
      <Step title="Confirm the Custom Integration configuration">
        Confirm your Custom Integration is saved with a Canvas URL and `admin_post` section in **Settings → Integrations → Custom**.
      </Step>

      <Step title="Verify your Canvas URL response">
        Confirm the Canvas URL responds to a POST with a 2xx status and a JSON body that is either an array or a single component object.
      </Step>

      <Step title="Check the browser console">
        Open the browser console while loading the feedback post — Sleekplan logs Canvas request and response errors there.
      </Step>
    </Steps>
  </Accordion>

  <Accordion title="X-Secret mismatch — my endpoint is rejecting requests">
    <Steps>
      <Step title="Compare the secret token exactly">
        Confirm the secret token in **Settings → Integrations → Custom** exactly matches what your server compares against. Trim any leading or trailing whitespace from both values.
      </Step>

      <Step title="Use a case-sensitive comparison">
        Check that the comparison is case-sensitive. The header value is sent exactly as stored — no normalization is applied.
      </Step>
    </Steps>
  </Accordion>

  <Accordion title="Submissions don't carry the field I expect">
    <Steps>
      <Step title="Check the component id, not the label">
        The submitted field is keyed by the component's `id`, not by `label` or `title`. Re-check the `id` values you returned in your Canvas response.
      </Step>

      <Step title="Review the interaction model for each component type">
        Buttons always submit `{ [id]: true }`. Lists submit `{ [id]: <selected item id> }`. See [Canvas components](/canvas/components) for the full interaction model.
      </Step>
    </Steps>
  </Accordion>

  <Accordion title="I need to allow-list Sleekplan's IP">
    Outbound Canvas calls originate from `168.119.102.99`. Allow this address on your firewall or WAF.
  </Accordion>
</AccordionGroup>

<Note>
  Still stuck? Contact our developer support team at [support@sleekplan.com](mailto:support@sleekplan.com).
</Note>
