> ## Documentation Index
> Fetch the complete documentation index at: https://ribaunt-e66481b6-mintlify-4e7c3afc.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Events

> Reference for all events emitted by ribaunt-widget: verify, error, state-change, and solver-backend. Includes React callback props and detail types.

The `<ribaunt-widget>` emits four DOM custom events — `verify`, `error`, `state-change`, and `solver-backend` — that you can listen to with `addEventListener` on the element. In React, use the typed callback props instead of `addEventListener`; the wrapper wires up and tears down listeners for you automatically.

## `verify`

The `verify` event is dispatched when the widget successfully solves all challenges and, if you provided a `verify-endpoint`, the server confirms the solutions are valid. If no `verify-endpoint` is set, the event fires as soon as local proof-of-work is complete.

**Event type:** `CustomEvent<{ solutions: ChallengeSolution[]; phase: 'done'; progress: 100 }>`

Where `ChallengeSolution = { nonce: string; hash: string }`.

```js theme={null}
widget.addEventListener('verify', (event) => {
  const { solutions } = event.detail;
  console.log('Verified! Solutions:', solutions);
  // Enable submit button, proceed with form, etc.
});
```

**React equivalent:** `onVerify={(detail) => ...}`

## `error`

The `error` event is dispatched when the widget encounters a failure at any stage — fetching tokens from your challenge endpoint, running the proof-of-work solver, or receiving a non-OK response from your verify endpoint.

**Event type:** `CustomEvent<{ error: string; code: WidgetErrorCode; timeout: boolean; phase: 'error' }>`

The `code` field is a stable, machine-readable identifier you can branch on:

| Code                     | Meaning                                                                                       |
| ------------------------ | --------------------------------------------------------------------------------------------- |
| `timeout`                | The attempt exceeded the configured `solve-timeout`. Covers fetching, solving, and verifying. |
| `aborted`                | Solving was cancelled (e.g. widget reset).                                                    |
| `challenge-fetch-failed` | Request to the challenge endpoint failed.                                                     |
| `invalid-challenge`      | Challenge response had an unexpected shape.                                                   |
| `solve-failed`           | Solver could not find a valid nonce.                                                          |
| `verification-failed`    | Server verify endpoint rejected the solution.                                                 |
| `worker-unavailable`     | Web Worker solver is not available and `worker-mode="required"` is set.                       |
| `unknown`                | Unclassified error.                                                                           |

```js theme={null}
widget.addEventListener('error', (event) => {
  const { error, code, timeout } = event.detail;
  if (timeout) {
    console.warn('Timed out:', error);
  } else {
    console.error('CAPTCHA failed:', error, code);
  }
});
```

**React equivalent:** `onError={(detail) => ...}`

<Note>
  The `timeout` field is always present in the event detail. It is `true` only when `solve-timeout` is configured and the attempt exceeded that limit; for every other error it is `false`.
</Note>

## `state-change`

The `state-change` event is dispatched every time the widget transitions between internal states. You can use this to mirror the widget's visual state in your own UI — for example, disabling a submit button while verification is in progress.

**Event type:** `CustomEvent<{ state: WidgetState; phase: WidgetState; progress: number }>`

```js theme={null}
widget.addEventListener('state-change', (event) => {
  const { state, progress } = event.detail;
  console.log('Widget state:', state, progress);
});
```

**React equivalent:** `onStateChange={(detail) => ...}`

## `solver-backend`

The `solver-backend` event is dispatched once per solve request when the worker selects its solving backend: `wasm` when the WebAssembly SHA-256 solver loaded, `js` when WASM is disabled or unavailable, or `argon2id` when the challenge tokens use the memory-hard [Argon2id algorithm](/api/create-challenge#argon2id-opt-in). Use it for adoption telemetry or to confirm your [`wasm-mode`](/widget/configuration#wasm-solver) configuration takes effect. The detail never includes challenge contents, nonces, or hashes.

**Event type:** `CustomEvent<{ backend: 'wasm' | 'js' | 'argon2id'; phase: 'solving' }>`

```js theme={null}
widget.addEventListener('solver-backend', (event) => {
  console.log('Solver backend:', event.detail.backend);
});
```

The event only fires when solving runs inside a Web Worker. When `worker-mode="preferred"` falls back to main-thread solving, no `solver-backend` event is emitted.

The worker detects the algorithm from the challenge tokens themselves, so `argon2id` is reported automatically whenever your server issues Argon2id challenges. `wasm-mode` does not affect this selection; it only controls the SHA-256 solver.

**React equivalent:** listen via `onEvent` or `addEventListener` on the element ref.

## React callback props

When you use the React wrapper, you can pass all callbacks directly as props. The wrapper maintains stable event listener references across re-renders so your callbacks always receive the latest closure values:

```tsx theme={null}
<RibauntWidget
  challengeEndpoint="/api/captcha/challenge"
  verifyEndpoint="/api/captcha/verify"
  onVerify={(detail) => console.log('Solutions:', detail.solutions)}
  onError={(detail) => console.error('Error:', detail.error)}
  onStateChange={(detail) => console.log('State:', detail.state)}
  onReady={(detail) => console.log('Ready with state:', detail.state)}
  onLoad={(detail) => console.log('Widget loaded:', detail.state)}
  onEvent={(type, detail) => console.log('Event:', type, detail)}
/>
```

## React-only events

Three additional callbacks are available in the React wrapper that have no corresponding DOM custom event on the web component:

* **`onReady`** — fires once after the widget mounts, with the initial widget state in the detail payload. Use this to know when the widget is ready for interaction.
* **`onLoad`** — functionally identical to `onReady`. It is provided as an alias for backward compatibility if you were already using `onLoad` in an earlier version.
* **`onEvent`** — a catch-all handler that fires for every event type (`'verify'`, `'error'`, `'state-change'`, `'solver-backend'`, and `'ready'`), along with the event's detail object. Use this if you want a single place to handle or log all widget events.

## Widget states

The widget moves through a defined set of states during its lifecycle. You will encounter these state strings in `state-change` events and in the return value of `getState()`:

| State       | Description                                              |
| ----------- | -------------------------------------------------------- |
| `initial`   | Widget has loaded, waiting for user click or auto-verify |
| `fetching`  | The widget is requesting challenges                      |
| `solving`   | The solver is running proof-of-work                      |
| `verifying` | The widget is posting solutions to your verify endpoint  |
| `done`      | Challenge solved and verified successfully               |
| `error`     | An error occurred during fetch, solve, or verify         |
