> For the complete documentation index, see [llms.txt](https://docs.limecall.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.limecall.com/callback/testing-and-debugging.md).

# Testing & debugging

How to test widget code, and what to check when something does not work.

## Test in a private window

A normal window may serve a cached page and may remember the widget was dismissed. Nearly every "it stopped working" report resolves here.

## Check the widget actually loaded

In the browser console:

```js
window.LimeCall && window.LimeCall.version
```

`undefined` means the script has not loaded — a snippet problem, not an API problem. See [The widget is not showing](/troubleshooting/widget-not-showing.md).

## Check its state

```js
window.LimeCall.getState()
// { ready: true, open: false, mode: "bubble", inWebCall: false, tab: null, visible: true }
```

`visible: false` means a display rule has hidden the widget on this page. `ready: false` means it is still initialising.

## Watch every event at once

The fastest way to see what the widget is doing:

```js
["ready","open","close","tab","teaser","score",
 "callback:requested","callback:failed","message:sent",
 "webcall:started","webcall:ended"].forEach(function (ev) {
  window.LimeCall.on(ev, function (payload) {
    console.log("[LimeCall]", ev, payload);
  });
});
```

Paste that, then use the widget normally. It answers most "why did nothing happen" questions in one pass.

## Do not trust the return value

Methods return `false` when the widget is not ready yet — but the call is still queued and replayed, up to 8 of them. A `false` is not a failure, and `true` is not a delivered callback.

For anything that matters, use the events — or `requestCallback()`, whose promise does resolve with the real outcome.

## Check whether anyone can answer

```js
await window.LimeCall.hasAvailableAgents();
```

`false` has several causes, and the method deliberately does not tell you which: outside working hours, nobody configured to route to, or no AI minutes left. To find out which one applies, open the Network tab and look at the response from `callback/availability` — it carries a `reason`:

| `reason`            | Means                                                                               |
| ------------------- | ----------------------------------------------------------------------------------- |
| `outside_hours`     | Working hours say closed.                                                           |
| `nobody_configured` | The widget routes to a person or team that no longer resolves.                      |
| `no_ai_minutes`     | AI minutes are exhausted and no overflow person is configured.                      |
| `overflow`          | Out of AI minutes, but a human answers — this one is paired with `available: true`. |

Remember the 20-second cache: after changing your hours or routing, wait it out, or reload the page, before re-testing.

## `getDepartments()` returns an empty array

You called it too early. Departments arrive with the widget's configuration, so the array is empty until `ready` fires. Move the call inside `on("ready", …)`.

## `setDepartment()` seems to be ignored

Two things override it, both intentionally:

* **The visitor's own choice.** If they pick a department in the form, theirs wins.
* **A label that does not match.** Compare against `getDepartments()` — the names must match exactly, and renaming a department in the dashboard breaks a hard-coded string.

## The `#limecall` link does nothing the second time

The anchor opens the widget on load and whenever the address changes. Clicking a link to the address you are already at changes nothing, so the browser fires no event. Use a click handler calling `open()` for a control the visitor may use repeatedly.

## A setting changes in the dashboard but not on your site

Check your page source for `data-` attributes beyond `data-key` and `data-api`.

Attributes override the fetched configuration and freeze that setting permanently. A snippet carrying `data-mode="bubble"` ignores every later change to Display style.

## The widget loads but calls never connect

That is routing, not the widget. Work through [Calls are not connecting](/troubleshooting/calls-not-connecting.md) — the usual causes are business hours, personal hours, or no destination configured.

## Requests are rejected

Listen for `callback:failed` and read `e.message`. `reason: "rejected"` is a deliberate refusal — closed hours, blocked number, unsupported country. `reason: "network"` is connectivity.

## Content Security Policy

A blocked script fails silently except for a console violation. Allow the host from your snippet in both directives:

```
script-src  https://dashboard.limephone.io
connect-src https://dashboard.limephone.io
```

## Testing on a local machine

The widget works on `localhost`. If you restrict by URL, remember your display rules apply to the local URL too — a rule matching your production path will hide the widget locally.

## Getting help

Include the page URL, the browser, the output of `getState()`, and any console errors. Those four turn a multi-day exchange into one reply.
