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

# Events

> Reference for all events emitted by ribaunt-widget: verify, error, and state-change. Includes React callback prop equivalents and event detail types.

The `<ribaunt-widget>` emits three DOM custom events — `verify`, `error`, and `state-change` — 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`                | Solving exceeded the configured `solve-timeout`. |
| `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.              |
| `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 only present in the event detail when `solve-timeout` is configured and the solving duration exceeds that limit. When present it is always `true`.
</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) => ...}`

## 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'`, 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         |
