2025-01-11 17:35:53 +00:00
---
outline: deep
---
2025-01-14 13:07:08 +00:00
# Quickstart
2025-04-12 13:39:56 +01:00
2025-01-14 13:29:22 +00:00
[[toc]]
2025-01-11 17:35:53 +00:00
2025-03-04 11:30:35 +00:00
## Requirements
2025-04-12 13:39:56 +01:00
- **Server-side library:** At least Node 14. Most modern Bun or Deno versions should work too. If you're using Glitch, make sure to set Node 14 or higher in your `engines` field in `package.json` . If you don't use a JavaScript runtime, consider using the [Standalone ](standalone.md ) server.
- **Client-side widget:** All modern browsers should be supported. A [compatibility version ](widget.md#compatibility-version ) is available too.
2025-03-04 11:30:35 +00:00
2025-01-14 13:29:22 +00:00
## Client-side
2025-01-15 16:46:07 +00:00
Start by adding importing the Cap widget library from a CDN:
2025-01-14 13:29:22 +00:00
2025-04-22 14:41:19 +01:00
::: code-group
```html [jsdelivr]
2025-01-14 13:29:22 +00:00
<script src="https://cdn.jsdelivr.net/npm/@cap.js/widget"></script>
` ``
2025-04-22 14:41:19 +01:00
` ``html [unpkg]
<script src="https://unpkg.com/@cap.js/widget"></script>
` ``
:::
2025-01-14 13:29:22 +00:00
Next, add the ` <cap-widget>` component to your HTML.
` ``html
2025-04-12 13:39:56 +01:00
<cap-widget
id="cap"
data-cap-api-endpoint="<your cap api endpoint>"
></cap-widget>
2025-01-14 13:29:22 +00:00
` ``
> [!NOTE]
2025-01-14 13:30:57 +00:00
> You'll need to start a server with the Cap API running at the same URL as specified in the ` data-cap-api-endpoint` attribute.
2025-01-14 13:29:22 +00:00
> In the server-side example we provided, it's set to ` /api`, but you can change this by replacing every ` app.post('/api/...', ...)` to ` app.post('/<endpoint>/...', ...)`.
Then, in your JavaScript, listen for the ` solve` event to capture the token when generated:
` ``js{3}
const widget = document.querySelector("#cap");
2025-04-12 13:39:56 +01:00
widget.addEventListener("solve", function (e) {
2025-01-14 13:29:22 +00:00
const token = e.detail.token;
2025-04-12 13:39:56 +01:00
2025-01-14 13:29:22 +00:00
// Handle the token as needed
});
` ``
2025-04-23 14:52:50 +01:00
Alternatively, you can use ` onsolve=""` directly within the widget or wrap the widget in a ` <form></form>` (where Cap will automatically submit the token alongside other form data. for this, it'll create a hidden field with name set to its ` data-cap-hidden-field-name` attribute or ` cap-token`).
2025-01-14 13:29:22 +00:00
## Server-side
2025-04-12 13:39:56 +01:00
2025-01-14 13:30:57 +00:00
Cap is fully self-hosted, so you'll need to start a server with the Cap API running at the same URL as specified in the ` data-cap-api-endpoint` attribute. This is easy since we've already pre-made a library to help you generate and validate challenges for you.
2025-01-12 12:42:41 +00:00
2025-04-14 12:59:05 +01:00
Start by installing it:
2025-01-13 20:01:26 +00:00
2025-04-14 12:59:05 +01:00
::: code-group
` ``bash [bun]
bun add @cap.js/server
2025-01-13 20:01:26 +00:00
` ``
2025-04-14 12:59:05 +01:00
` ``bash [npm]
2025-01-13 20:01:26 +00:00
npm i @cap.js/server
2025-01-12 12:42:41 +00:00
` ``
2025-04-14 12:59:05 +01:00
` ``bash [pnpm]
pnpm i @cap.js/server
` ``
2025-04-14 13:00:45 +01:00
:::
2025-01-14 16:07:02 +00:00
> [!NOTE]
> It is recommended to use at least Node.js 14 or Bun 1.0.0. You might experience multiple issues on older versions of these runtimes.
2025-04-12 13:39:56 +01:00
Now, you'll need to change your server code to add the routes that Cap needs to work. Here's an example:
2025-01-12 12:42:41 +00:00
2025-04-12 13:39:56 +01:00
::: code-group
` ``js [Elysia]
import { Elysia } from "elysia";
import Cap from "@cap.js/server";
const cap = new Cap({
tokens_store_path: ".data/tokensList.json",
});
new Elysia()
.post("/api/challenge", () => {
return cap.createChallenge();
})
.post("/api/redeem", async ({ body, set }) => {
const { token, solutions } = body;
if (!token || !solutions) {
set.status = 400;
return { success: false };
}
return await cap.redeemChallenge({ token, solutions });
})
.listen(3000);
console.log(` 🦊 Elysia is running at http://localhost:3000`);
` ``
` ``js [Fastify]
import Fastify from "fastify";
import Cap from "@cap.js/server";
const fastify = Fastify();
const cap = new Cap({
tokens_store_path: ".data/tokensList.json",
});
fastify.post("/api/challenge", (req, res) => {
res.send(cap.createChallenge());
});
fastify.post("/api/redeem", async (req, res) => {
const { token, solutions } = req.body;
if (!token || !solutions) {
return res.code(400).send({ success: false });
}
res.send(await cap.redeemChallenge({ token, solutions }));
});
fastify.listen({ port: 3000, host: "0.0.0.0" }).then(() => {
console.log("Server is running on http://localhost:3000");
});
` ``
` ``js [Bun.serve]
import Cap from "@cap.js/server";
const cap = new Cap({
tokens_store_path: ".data/tokensList.json",
});
Bun.serve({
port: 3000,
routes: {
"/api/challenge": {
POST: () => {
return Response.json(cap.createChallenge());
},
},
"/api/redeem": {
POST: async (req) => {
const body = await req.json();
const { token, solutions } = body;
if (!token || !solutions) {
return Response.json({ success: false }, { status: 400 });
}
return Response.json(await cap.redeemChallenge({ token, solutions }));
},
},
},
});
console.log(` Server running at http://localhost:3000`);
` ``
` ``js [Express]
import express from "express";
import Cap from "@cap.js/server";
2025-01-12 12:42:41 +00:00
2025-01-13 20:01:26 +00:00
const app = express();
app.use(express.json());
const cap = new Cap({
2025-04-12 13:39:56 +01:00
tokens_store_path: ".data/tokensList.json",
2025-01-13 20:01:26 +00:00
});
2025-04-12 13:39:56 +01:00
app.post("/api/challenge", (req, res) => {
2025-01-13 20:34:11 +00:00
res.json(cap.createChallenge());
2025-01-13 20:01:26 +00:00
});
2025-04-12 13:39:56 +01:00
app.post("/api/redeem", async (req, res) => {
2025-01-13 20:01:26 +00:00
const { token, solutions } = req.body;
if (!token || !solutions) {
return res.status(400).json({ success: false });
}
res.json(await cap.redeemChallenge({ token, solutions }));
});
app.listen(3000, () => {
2025-04-12 13:39:56 +01:00
console.log("Listening on port 3000");
});
2025-01-12 12:42:41 +00:00
` ``
2025-01-18 13:46:25 +00:00
2025-04-12 13:39:56 +01:00
:::
2025-01-13 20:01:26 +00:00
2025-01-14 13:29:22 +00:00
### Token Validation
2025-01-11 17:35:53 +00:00
2025-01-14 13:29:22 +00:00
Once the token is generated and captured, you can use it later to validate the user's identity. You can do this by calling ` await cap.validateToken` in your server-side code:
2025-01-14 13:07:08 +00:00
` ``js
2025-04-12 13:39:56 +01:00
await cap.validateToken("..."); // returns { success: Boolean }
2025-01-14 13:07:08 +00:00
` ``
2025-04-14 12:59:05 +01:00
Note that the token will immediately be deleted after this. To prevent this, use ` await cap.validateToken("...", { keepToken: true })`.