---
type: reference
title: Widget methods and events
summary: Every method you can call on the Caputchin widget elements and every event they emit, with the exact payload of each, which element exposes it, and when it is valid.
---

The Caputchin widget ships two custom elements, and your page interacts with them in two directions: you **call methods** on the element to drive it, and you **listen for events** it emits to react. This page is the exhaustive reference for both surfaces.

Every event is a `CustomEvent` dispatched with `bubbles: true` and `composed: true`, so it escapes the widget's shadow DOM and you can listen for it directly on the element (or on any ancestor). The data is always on `event.detail`.

## The two elements at a glance

| Element | Purpose | Methods |
|---|---|---|
| `<caputchin-widget>` | Verification only (proof-of-work / checkbox). | `start()` |
| `<caputchin-game>` | Game host, with optional verification. | `pass()`, `fail()` |

The checkbox widget has only `start()`; it does **not** expose `pass()` / `fail()`. The game host has no `start()` (its round becomes live on mount for inline, or on the first dialog open for modal / fullscreen). Both elements emit `start`, `pass`, `error`, and `degraded`; the game host additionally emits `dialog-shown` and `dialog-hidden`. Coverage is in the [events table](#events) below.

## Methods

### `start()` (`<caputchin-widget>` only)

Begins verification immediately. Its purpose is the **manual trigger**: when the element has `trigger="manual"`, you call `start()` to fire the proof-of-work solve from your own gesture (for example, on your form's submit) instead of the built-in checkbox. With the default `auto` / `click` triggers the element wires its own activation, so you do not call this.

```js
const widget = document.querySelector("caputchin-widget");
widget.start();
```

The game host has no `start()`. See [manual mode](/docs/custom-game-development/manual-mode) for the full driving-it-yourself walkthrough.

### `pass(payload)` (`<caputchin-game>` only)

Releases the verification gate: the visitor succeeded. **Valid only when `trigger="manual"`**; calling it on a non-manual game emits an `error` with code `invalid-call`. The first `pass()` redeems and **locks** the verdict; later calls are silently ignored (one round per session).

```js
const widget = document.querySelector("caputchin-game");
widget.pass({ trace: roundRecord });
```

| `payload` field | Type | Meaning |
|---|---|---|
| `trace` | `string` | The opaque round record your game produces. On a gated round the server replays it to derive the authoritative verdict; on an ungated or game-only key it is accepted as-is. Pass an empty string if your interaction has no record to replay. |

There is no `score` field on this method, the gate is the server's replay of the `trace`, not a client-claimed score. (A `score` does appear on the [`pass` event](#the-pass-event-and-the-token), which is the widget reporting back to you, not you to the widget.)

Calling `pass()` before the round has started (modal / fullscreen, dialog not yet opened) emits an `error` with code `invalid-call`. On a game-only key (no `sitekey`) there is no gate to release, so `pass()` simply emits the `pass` event with `token: null`.

### `fail(payload?)` (`<caputchin-game>` only)

Aborts the round: a definitive loss. **Valid only when `trigger="manual"`** (otherwise `invalid-call`). Optional, an unfinished round is treated as a non-pass anyway. It aborts the in-flight verification (the proof-of-work and instrumentation checks) and surfaces an `error` event with code `game-error-relayed`.

```js
widget.fail({ code: "out-of-moves", message: "no moves left" });
```

| `payload` field | Type | Meaning |
|---|---|---|
| `code` | `string` (optional) | Your diagnostic code; surfaced as the `error` event's `originalCode`. Defaults to `game-failed`. |
| `message` | `string` (optional) | Human-readable reason. |

## Events [#events]

Listen with `addEventListener(type, handler)`. Every payload lives on `event.detail`.

| Event | Emitted by | `event.detail` | Fires when |
|---|---|---|---|
| `start` | both | `{ gameId: string \| null }` | Verification begins (auto on mount, on activation, or via `start()`). `gameId` is set for a game round, `null` for the plain widget. |
| `pass` | both | `{ token: string \| null, score: number \| null, durationMs: number \| null }` | Verification is released. `token` is the wrapped token to send to your backend; `null` on a game-only key (no `sitekey`). |
| `error` | both | `{ code, message, severity, originalCode? }` | Anything from a benign config warning to a hard failure. See [the error event](#the-error-event). |
| `degraded` | both | `{ reason: "timeout" \| "network" \| "http" \| "malformed" }` | The widget could not resolve your configured size / skin / locale in time, so it rendered with its bundled defaults. Not an error. See [the degraded event](#the-degraded-event). |
| `dialog-shown` | `<caputchin-game>` | `{ layout: "modal" \| "fullscreen" }` | An overlay game dialog opens (programmatic or first click). |
| `dialog-hidden` | `<caputchin-game>` | `{ layout: "modal" \| "fullscreen" }` | An overlay game dialog closes (programmatic, Escape, or backdrop click). |

### The `pass` event and the token [#the-pass-event-and-the-token]

The `pass` event is your cue that verification succeeded, but it is **not** the trust decision. The trust decision is your backend confirming `detail.token`. Always [verify the token on your backend](/docs/integration-guides/verify-on-your-backend); never grant access off the front-end event alone. In a form, the widget also injects the token as a `caputchin-token` field so a normal form POST carries it. The `score` and `durationMs` on this event are client-reported analytics and may be `null`; never treat them as a trust signal.

### The `error` event [#the-error-event]

The `error` event carries a stable `code`, a human `message`, a `severity`, and sometimes an `originalCode`. Each code has a fixed default severity so you can filter "kept running" warnings from "actually broke" failures **without** hardcoding a code-to-severity table, just read `detail.severity`. The full set of codes:

| `code` | `severity` | Meaning |
|---|---|---|
| `invalid-config` | `warn` | An attribute or combination the widget rejected; it degrades gracefully and keeps running. |
| `invalid-call` | `warn` | A method called when it was not valid (e.g. `pass()` on a non-manual game, or before the round started). |
| `verification-failed` | `error` | The proof-of-work or instrumentation check (or the token redeem) failed; no token issued. |
| `game-load-failed` | `error` | The game bundle could not be resolved, loaded, or registered. |
| `gate-unavailable` | `error` | The server returned an authoritative refusal at bootstrap (a gated key could not supply a valid game). |
| `game-error-relayed` | `error` | An error surfaced from inside the game (relayed from the iframe, or from a manual `fail()`). |

`detail.originalCode` is present when the public `code` is a generalization of a more specific internal reason (for example, an iframe load failure relayed as `game-load-failed` carries the raw `iframe-load-failed` / `iframe-script-blocked` / `game-not-registered` in `originalCode`). Use `code` for branching and `originalCode` only for diagnostics.

### The `degraded` event [#the-degraded-event]

On mount the widget makes one short call to Caputchin to resolve the size, skin, and locale you configured (and, for a marketplace game, its preferred footprint). If that call fails or is too slow, the widget does not block or break: it renders with the values baked into the bundle and emits `degraded` so the fallback is never silent. The widget stays fully functional after a `degraded` event, only the resolved presentation may differ from what you configured (a game may appear at its default size, or the checkbox in its default theme). The widget retries a slow resolve before falling back, so `degraded` means the retries were exhausted, not that one attempt was slow.

`detail.reason` tells you why the resolve fell back, so you can surface it in your own telemetry:

| `reason` | Meaning |
|---|---|
| `timeout` | The resolve did not answer within its time budget (service slow or unreachable). |
| `network` | The request itself failed (offline, DNS, blocked, or connection reset). |
| `http` | The service answered with an error status. |
| `malformed` | The service answered, but the body was not valid. |

`degraded` is a notice, not a failure, so it is deliberately **not** an `error` event: a slow resolve should not trip the handlers you wire for real failures. Listen for it only if you want to observe degraded renders, for example to alert when your visitors are seeing fallback presentation.

## Listening, in practice

```js
const widget = document.querySelector("caputchin-game");

const onPass = (e) => {
  // send e.detail.token to your backend to verify
};
const onError = (e) => {
  if (e.detail.severity === "error") {
    console.error(e.detail.code, e.detail.message);
  }
};

widget.addEventListener("pass", onPass);
widget.addEventListener("error", onError);

// Clean up when you tear down your view:
widget.removeEventListener("pass", onPass);
widget.removeEventListener("error", onError);
```

These are native DOM `CustomEvent`s, so in React you attach them with a `ref` and `addEventListener` (not JSX `onPass` props); the [frontend examples](/docs/integration-guides/frontend-examples) show framework-specific wiring. Because every event bubbles and is composed, you can also delegate from a container element rather than binding each widget individually.

## See also

- [Add the widget](/docs/getting-started/add-the-widget): the configuration attributes you set in markup (the input side to this output side).
- [Drive verification yourself with manual mode](/docs/custom-game-development/manual-mode): the tutorial that uses `start()` / `pass()` / `fail()`.
- [Verify on your backend](/docs/integration-guides/verify-on-your-backend): what to do with the `pass` event's token.
- [Frontend examples](/docs/integration-guides/frontend-examples): wiring these events in React, Vue, and plain JS.
