| title | Quickstart |
|---|---|
| sidebarTitle | Quickstart |
| description | Install Ribaunt, configure your secret, issue signed challenges, verify the submitted proofs, and embed the browser widget in minutes. |
This guide walks you through building a complete Ribaunt CAPTCHA integration: two server endpoints that issue and verify proof-of-work challenges, plus a browser widget your users interact with. By the end you'll have a working bot-protection layer you can drop in front of any form or API action.
Add the `ribaunt` package to your project.<CodeGroup>
```bash npm
npm install ribaunt
```
```bash yarn
yarn add ribaunt
```
```bash pnpm
pnpm add ribaunt
```
</CodeGroup>
```env
RIBAUNT_SECRET="replace-with-a-long-random-secret"
```
<Warning>
Keep `RIBAUNT_SECRET` server-only. Never expose it to the browser. In Next.js, do **not** prefix it with `NEXT_PUBLIC_` — that would embed the secret in your client bundle and allow anyone to forge valid challenge tokens.
</Warning>
```ts
import 'dotenv/config';
import express from 'express';
import { createChallenge, verifySolution } from 'ribaunt';
const app = express();
app.use(express.json());
app.get('/api/captcha/challenge', async (_req, res) => {
const challenges = await createChallenge({
difficulty: 5,
amount: 4,
ttlSeconds: 120,
context: 'signup',
});
res.json({ challenges });
});
app.post('/api/captcha/verify', async (req, res) => {
const { tokens, solutions } = req.body;
const result = await verifySolution(tokens, solutions, { context: 'signup' });
if (!result.valid) {
return res.status(400).json({ success: false, error: result.reason, message: result.message });
}
return res.json({ success: true });
});
app.listen(3000);
```
`createChallenge()` is asynchronous and returns `Promise<ChallengeToken[]>`, so always `await` it. It accepts either positional arguments or an options object. The options object supports `difficulty`, `amount`, `ttlSeconds`, `context`, an adaptive `workload` configuration, and the optional `rateLimiter` and `onEvent` hooks described in the [API reference](/api/create-challenge).
`verifySolution()` returns a structured result object instead of a bare boolean. Check `result.valid` and use `result.reason` or `result.message` when verification fails.
```html
<script type="module" src="/node_modules/ribaunt/dist/widget-browser.js"></script>
<ribaunt-widget
challenge-endpoint="/api/captcha/challenge"
verify-endpoint="/api/captcha/verify"
auto-verify="true"
solve-timeout="15000"
></ribaunt-widget>
<script>
const widget = document.querySelector('ribaunt-widget');
widget.addEventListener('verify', (event) => {
console.log('Verified!', event.detail.solutions);
});
</script>
```
If you're using React, import the wrapper component instead of the web component directly. See [React Integration](/integrations/react) for the full example.