Files
2026-09-07 00:52:16 -03:00

619 lines
22 KiB
Plaintext

---
title: Quick Start
description: Integrate Duckity in less than 5 minutes.
icon: Zap
---
Getting started with Duckity is quite simple. Head over to
[Duckity's Dashboard](https://app.duckity.com/) and log in using your favorite method. If it's your
first time logging in, your account will be automatically created.
## Create an Application [step]
Duckity is divided into one or more applications within your account. You create one application per
project in which you want to integrate Duckity. To create a new application, press on the yellow **+
New Application** button at the top right corner of the screen.
Once you've set the name of it, press on the **✓ Create Application** button. Congratulations! You
now have your first application created.
<Callout>
Additionally, in case your application is a web app, you can set up the CORS origins to fit your
application's origins. For example, if you'll be protecting the login form of your app hosted at
`https://example.com/login`, add `https://example.com` (scheme and host, no path) to your app's
origins. If your application is not a web app, you can leave the CORS origins empty.
</Callout>
## Create a Protection Profile [step]
Applications commonly have multiple features that need to be protected. For example, you may have a
signup form and a login form with different protection needs. For each, you'll create one
**protection profile** with settings tuned to each feature's specific needs.
Each application needs at least one protection profile to work. To create your first one, press the
**+ Create Protection Profile** button.
Set the name to the name of the feature you'll be protecting, like "Sign-up Form" or "Add Friend".
This is for you to recognize it later, it won't be displayed to your users. Once you're done filling
it, press on **✓ Create Protection Profile**.
Copy your protection profile's ID using the button at the top right corner, that's all you'll need
to integrate it into your application.
<Callout>
Don't worry about all the settings displayed for now, you'll learn to tune them later.
</Callout>
## Install a Client SDK [step]
To integrate your protection profile into your application, you'll need to install a client SDK in
your client and validate solution tokens from your server. This is very simple to do and will take
you little to no time.
<Tabs items={["JavaScript", "React", "Python", "Rust", "Other"]} groupId="sdk-language">
<Tab value="JavaScript">
<Tabs items={["Via NPM", "Via CDN"]}>
<Tab>
Run the following line in your terminal to install the Duckity SDK.
```package-install
@duckity/js
```
Then import it in your code:
```ts
import * as duckity from "@duckity/js";
```
</Tab>
<Tab>
If you're using the SDK from a static site, import it using a CDN like
[esm.sh](https://esm.sh/) instead.
```html
<script type="module">
// Using esm.sh
import * as duckity from "https://esm.sh/@duckity/js";
// Using jsdelivr.net
import * as duckity from "https://cdn.jsdelivr.net/npm/@duckity/js";
// Using UNPKG
import * as duckity from "https://unpkg.com/@duckity/js";
</script>
```
</Tab>
</Tabs>
Once you've imported the SDK, solve a challenge like follows:
```ts
async function handleSubmit(e) {
e.preventDefault();
const solution: string = await duckity.solve(PROTECTION_PROFILE_ID);
// Send it to your backend.
}
```
Send that solution token to your backend server. It's usually a good idea to send it in a
`X-Duckity-Solution` header in your request, for example
```ts
await fetch("/api/login", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Accept": "application/json",
"X-Duckity-Solution": solution
},
body: JSON.stringify({
login: username,
password: password,
}),
});
```
You can read about advanced usage in the SDK's documentation page:
<Card href="/sdks/javascript" title="JavaScript" icon={<SiJavascript />}>
Integrate Duckity into your application using the JavaScript SDK.
</Card>
</Tab>
<Tab value="React">
Run the following line in your terminal to install the Duckity SDK.
```package-install
@duckity/react
```
Then import the React hook in your code:
```ts
import { useChallenge } from "@duckity/react";
```
You can then use the hook in your client components:
<Tabs items={["Next.js", "Vite", "Remix", "Gatsby", "Expo Web", "Astro"]} groupId="react-framework" persist>
<Tab value="Next.js">
```ts
"use client";
import { useChallenge } from "@duckity/react";
function MyComponent() {
const duckity = useChallenge(process.env.NEXT_PUBLIC_DUCKITY_PROTECTION_PROFILE_ID);
}
```
</Tab>
<Tab value="Vite">
```ts
import { useChallenge } from "@duckity/react";
function MyComponent() {
const duckity = useChallenge(import.meta.env.VITE_DUCKITY_PROTECTION_PROFILE_ID);
}
```
</Tab>
<Tab value="Remix">
```ts
import { json } from "@remix-run/node"; // Or cloudflare/deno
import { useLoaderData } from "@remix-run/react";
import { useChallenge } from "@duckity/react";
export async function loader() {
return json({
ENV: {
DUCKITY_PROTECTION_PROFILE_ID: process.env.DUCKITY_PROTECTION_PROFILE_ID,
},
});
}
function MyComponent() {
const data = useLoaderData<typeof loader>();
const duckity = useChallenge(data.ENV.DUCKITY_PROTECTION_PROFILE_ID);
}
```
</Tab>
<Tab value="Gatsby">
```ts
import { useChallenge } from "@duckity/react";
function MyComponent() {
const duckity = useChallenge(process.env.GATSBY_DUCKITY_PROTECTION_PROFILE_ID);
}
```
</Tab>
<Tab value="Expo Web">
```ts
import { useChallenge } from "@duckity/react";
function MyComponent() {
const duckity = useChallenge(process.env.EXPO_PUBLIC_DUCKITY_PROTECTION_PROFILE_ID);
}
```
</Tab>
<Tab value="Astro">
```ts
import { useChallenge } from "@duckity/react";
function MyComponent() {
const duckity = useChallenge(import.meta.env.PUBLIC_DUCKITY_PROTECTION_PROFILE_ID);
}
```
This is a client component, so use it as follows when rendering it:
```tsx
<MyComponent client:load />
```
</Tab>
</Tabs>
When the component is first rendered, it'll start fetching and solving a challenge.
You can then wait for and use a solution token:
```ts
async function handleSubmit(e) {
e.preventDefault();
const solution = await duckity.wait();
// Send the solution token to your backend.
}
```
Send that solution token to your backend server. It's usually a good idea to send it in a
`X-Duckity-Solution` header in your request, for example
```ts
await fetch("/api/login", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Accept": "application/json",
"X-Duckity-Solution": solution
},
body: JSON.stringify({
login: username,
password: password,
}),
});
```
You can read about advanced usage in the SDK's documentation page:
<Card href="/sdks/react" title="React" icon={<SiReact />}>
Integrate Duckity into your application using the React SDK.
</Card>
</Tab>
<Tab value="Python">
Install the SDK from PyPI using your favorite package manager:
{/* prettier-ignore */}
<Tabs items={["pip", "uv", "poetry"]} groupId="python-package-manager" persist>
<Tab value="pip">
```sh
pip install duckity
```
</Tab>
<Tab value="uv">
```sh
uv add duckity
```
</Tab>
<Tab value="poetry">
```sh
poetry add duckity
```
</Tab>
</Tabs>
Solving a challenge only requires your protection profile ID.
<Tabs items={["Async", "Sync"]} groupId="python-sync" persist>
<Tab value="Async">
```py
import duckity
solution: str = await duckity.solve(PROTECTION_PROFILE_ID)
```
The CPU-intensive part of solving the challenge is done in a
`ProcessPoolExecutor()`. Neither the GIL nor the async event loop will be blocked.
</Tab>
<Tab value="Sync">
```py
import asyncio
import duckity
solution: str = asyncio.run(duckity.solve(PROTECTION_PROFILE_ID))
```
The CPU-intensive part of solving the challenge is done in a
`ProcessPoolExecutor()`. The GIL will not be blocked.
</Tab>
</Tabs>
Once you have a solution token, send it to your backend for validation. It's usually a good
idea to send it in a `X-Duckity-Solution` request header, like follows:
```py
import requests
solution: str = asyncio.run(duckity.solve(PROTECTION_PROFILE_ID))
requests.post(
"https://api.example.com/login",
headers={
"Accept": "application/json",
"X-Duckity-Solution": solution,
},
json={
"login": username,
"password": password
}
)
```
You can read about advanced usage in the SDK's documentation page:
<Card href="/sdks/python" title="Python" icon={<SiPython />}>
Integrate Duckity into your application using the Python SDK.
</Card>
</Tab>
<Tab value="Rust">
Run the following cargo command in your terminal to add the `duckity` crate to your
project:
```sh
cargo add duckity
```
The crate depends on `tokio`, you can use `async-compat` to make it work in `smol`
contexts.
To get and solve a challenge, use `duckity::solve()` like follows:
```rs
let solution: str = duckity::solve(PROTECTION_PROFILE_ID).await?;
```
That's it! Once you have your solution token, you can send it over to your backend for
validation. It's usually a good idea to send it in a `X-Duckity-Solution` request header,
like follows:
```rs
reqwest::Client::new()
.post("https://api.example.com/login")
.header("x-duckity-solution", solution)
.json(&Login {
login: username,
password: password,
})
.send()
.await?;
```
You can read about advanced usage in the SDK's documentation page:
<Card href="/sdks/rust" title="Rust" icon={<SiRust />}>
Integrate Duckity into your application using the Rust SDK.
</Card>
</Tab>
<Tab value="Other">
In case your platform is a different one, we provide a detailed step-by-step guide on how
to write your own client-side SDK implementation in any language, with code examples and
pseudocode. It's written so that anyone can implement a working client-side implementation
for any platform, so if you have a bit of time, you may be able to get it working in less
than an hour.
<Card href="/guides/write-your-own-sdk" title="Write Your Own SDK" icon={<LiPencil />}>
Integrate Duckity into your application in any programming language and platform.
</Card>
We constantly improve this guide with incoming feedback from developers like you. If you
find anything that could be improved, let us know!
Additionally, in case anything in the guide is not clear, open a support ticket in the
dashboard and we'll try to help you out. Note that we're not experts in every language nor
platform, so we likely won't be able to debug your code, rather help you understand what
the guide says.
</Tab>
</Tabs>
## Install a Server SDK [step]
Once you have a solution token in your server, validating it is quite easy.
<Tabs items={["JavaScript", "Python", "Rust", "Manual HTTP"]} groupId="sdk-language">
<Tab value="JavaScript">
Run the following line in your terminal to install the Duckity SDK.
```package-install
@duckity/js
```
Then import it in your code:
```ts
import * as duckity from "@duckity/js";
```
To validate the solution token, use `duckity.validate()`.
```ts
let isValid = await duckity.validate(
solution, // The solution token submitted by the client.
clientIp, // The IP address of the client that submitted the solution.
applicationSecret, // Your application's secret. Get it from the dashboard.
protectionProfileId, // The protection profile ID used to generate the challenge.
);
```
That's it! If the solution is not valid, reject the request with an error. Otherwise,
proceed to process the rest of the request.
</Tab>
<Tab value="Python">
Install the SDK from PyPI using your favorite package manager:
{/* prettier-ignore */}
<Tabs items={["pip", "uv", "poetry"]} groupId="python-package-manager" persist>
<Tab value="pip">
```sh
pip install duckity
```
</Tab>
<Tab value="uv">
```sh
uv add duckity
```
</Tab>
<Tab value="poetry">
```sh
poetry add duckity
```
</Tab>
</Tabs>
Once you have done it, validate the solution token as follows:
<Tabs items={["Async", "Sync"]} groupId="python-sync" persist>
<Tab value="Async">
```py
import duckity
solution: str # The solution token submitted by the client.
client_ip: str # The IP address of the client that submitted the solution.
application_secret: str # Your application's secret. Get it from the dashboard.
protection_profile_id: str # The protection profile ID used to generate the challenge.
is_valid: bool = await duckity.validate(
solution, client_ip, application_secret, protection_profile_id
)
```
</Tab>
<Tab value="Sync">
```py
import asyncio
import duckity
solution: str # The solution token submitted by the client.
client_ip: str # The IP address of the client that submitted the solution.
application_secret: str # Your application's secret. Get it from the dashboard.
protection_profile_id: str # The protection profile ID used to generate the challenge.
is_valid: bool = asyncio.run(
duckity.validate(solution, client_ip, application_secret, protection_profile_id)
)
```
</Tab>
</Tabs>
That's it! If the solution token is valid, you can proceed to process your request. If it's
not, return an error to the client and do not further process the request.
</Tab>
<Tab value="Rust">
Run the following cargo command in your terminal to add the `duckity` crate to your
project:
```sh
cargo add duckity
```
Once you have it installed, you can validate solution tokens as follows:
```rs
let is_valid: bool = duckity::validate(
solution, // The solution token submitted by the client.
client_ip, // The IP address of the client that submitted the solution.
APPLICATION_SECRET, // Your application's secret. Get it from the dashboard.
PROTECTION_PROFILE_ID // The protection profile ID used to generate the challenge.
)
.await?;
```
That's it! If the solution token is valid, you can proceed to process your request. If it's
not, return an error to the client and do not further process the request.
</Tab>
<Tab value="Manual HTTP">
In case you cannot or do not want to install an SDK, you can validate solution tokens by
hand by making the HTTP request by hand instead.
```http
POST /d1/challenges/{protection_profile_id}/validate HTTP/1.1
Host: api.duckity.com
Accept: application/json
Content-Type: application/json
Authorization: Bearer {application_secret}
{
"solution": "<solution-token-submitted-by-the-client>",
"ip": "<ip-address-of-the-client-that-submitted-the-solution>"
}
```
Validation responses are simple:
<Tabs items={["200", "401", "404", "422", "429", "500"]}>
<Tab value="200">
```http
HTTP/1.1 200 OK
Content-Type: application/json
{
"is_valid": true // or false
}
```
</Tab>
<Tab value="401">
```http
HTTP/1.1 401 Unauthorized
Content-Type: application/json
{
"title": "Invalid Application Secret",
"message": "The application secret you provided was not valid."
}
```
</Tab>
<Tab value="404">
```http
HTTP/1.1 404 Not Found
Content-Type: application/json
{
"title": "Protection Profile Not Found",
"message": "The protection profile ID you specified was not found."
}
```
</Tab>
<Tab value="422">
```http
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json
{
"title": "Invalid Request Body",
"message": "Are you sure the JSON body you sent in the request was valid?"
}
```
</Tab>
<Tab value="429">
```http
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
{
"title": "Too Many Requests",
"message": "Wohoah, you're going too fast, buddy. Slow down a bit."
}
```
</Tab>
<Tab value="500">
```http
HTTP/1.1 500 Internal Server Error
Content-Type: application/json
{
"title": "Internal Server Error",
"message": "The server crashed. Please try again."
}
```
</Tab>
</Tabs>
That's it! If the solution token is valid, you can proceed to process your request. If it's
not, return an error to the client and do not further process the request.
Note that status codes are related to server actions, not to client actions. An invalid
solution token submitted by the client will return `200` because the token was validated
successfully (even if the result was that it was invalid), while a wrong application secret
or an incorrect protection profile ID will return `4xx` status codes.
</Tab>
</Tabs>
## Next Steps
Once you have successfully integrated duckity into your application, you're done with the code part.
The behavior of challenges issued is fully customizable from the Duckity dashboard. Almost every
configuration can be easily understood by their dashboard description. However, to learn how to
combine settings to produce a more powerful setup, check out the following guide. It goes
configuration by configuration to teach you how to configure the parameters you need to suit your
application's needs.
<Card
href="/guides/tune-a-protection-profile"
title="Tune a Protection Profile"
icon={<LiPencil />}
>
Learn how to tune your protection profiles to better protect your application while enhancing
your users' experiences.
</Card>