> For the complete documentation index, see [llms.txt](https://docs.disasm.dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.disasm.dev/kasada/general-information.md).

# Overview

Read this page in full before integrating Kasada. It covers what the API computes, what stays on your side, and the vocabulary the rest of these guides assumes.

{% hint style="success" %}
Base URL: **`https://ksd.antibotapi.com`**
{% endhint %}

Every request requires your API key in the `x-api-key` header - see [Authentication](/getting-started/authentication.md).

## What Kasada is

Kasada protects a site with a JavaScript sensor rather than a cookie challenge. A protected origin serves four endpoints under a per-customer path: `/mfc` for first contact, `/fp` for the challenge page, `/ips.js` for the interrogation script, and `/tl` for the payload that script produces. The script is regenerated per session and heavily obfuscated, and it interrogates the browser it is running in - screen, GPU, audio stack, fonts, locale, timezone - then enciphers the answers into the body of a `POST` to `/tl`. Accepting that body is what mints the session tokens the site's own API then demands.

Those tokens are the `x-kpsdk-*` header family, and every request to a protected endpoint afterwards carries them, plus a small proof of work.

## How our API works

* **No Kasada JavaScript is executed.** We decompile the script you send us, recover its handler, and build the payload directly.
* **It computes; it never connects.** We do not fetch the block page, the script, or anything else, and we never post to the target. You already hold the session - the cookies, the proxy, the tokens - and a solver that fetched its own script would be solving a different session's script than the one your cookies belong to.
* **Synchronous.** A script this process has not seen costs around 200 ms of CPU; the same build again is 11-19 ms, because the recovery is cached per build and only the assembly is paid per request.
* **Self-updating.** Kasada rotates the script build frequently; the solver tracks it with no action on your side.
* **Chrome on Windows only.** Every device the payload draws from is a Windows Chrome capture, so the platform, screen, GPU and font list describe a Windows machine whatever user agent you send. Anything else is refused rather than billed - see [Payload](/kasada/payload.md).

Your HTTP client still has to match the browser you are claiming to be at the TLS and HTTP/2 level - see [TLS & Fingerprinting](/getting-started/tls-fingerprinting.md).

## The token vocabulary

Every one of these travels as an `x-kpsdk-` header. Knowing which are computed and which are echoed is most of the integration.

| Token | Where it comes from                                                                                                                          | What you do with it                                                                                                                                                      |
| ----- | -------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `ct`  | The `KP_UIDz` query parameter on the `ips.js` link. We read it off the link and return it.                                                   | Send it on the `/tl` POST. The `/tl` **response** carries a rotated `ct` - that newer one is what the protected request and the proof of work use.                       |
| `dt`  | Computed by us, from encoder constants recovered out of the exact script build you sent.                                                     | Send it on the `/tl` POST. It is bound to that build, so it cannot be reused across a rotation.                                                                          |
| `v`   | The `x-kpsdk-v` query parameter on the `ips.js` link.                                                                                        | Send it on every request in the flow. It is the SDK version the whole handshake declares.                                                                                |
| `im`  | The `x-kpsdk-im` query parameter on the `ips.js` link, which the challenge page originally posted to `window.parent` as `KPSDK:MC:<im>:...`. | Send it on the `/tl` POST.                                                                                                                                               |
| `r`   | A `/tl` **response** header.                                                                                                                 | Returned empty, because we do not make that request. Do not send it.                                                                                                     |
| `dv`  | Not set by any `ips.js` build we have seen, so there is nothing to recover it from.                                                          | Returned empty. Do not send it.                                                                                                                                          |
| `h`   | An `/mfc` **response** header. We return the literal `01`, which is what a browser sends **on** `/mfc`, before any token exists to echo.     | Not sent on `/tl`. It belongs to the protected request, and there you send the value your own `/mfc` response served - a browser echoes that one unchanged from then on. |
| `fc`  | An `/mfc` **response** header.                                                                                                               | Returned empty. It carries feature flags and a log level and is not an input to anything we compute.                                                                     |
| `cd`  | Computed by us from a separate call - see [Proof of work](/kasada/proof-of-work.md).                                                         | Send it on every request to the site's own protected endpoints. One fresh blob per request.                                                                              |

The empty ones - `r`, `dv` and `fc` - are present in the response rather than absent, so a client iterating the map does not have to special-case their shape. **Do not send a header whose value is empty.** An empty value and an absent field are different things on the wire, and the sensor reads the difference.

{% hint style="warning" %}
`ct` rotates. The token on the `ips.js` link is the one you present to `/tl`; the token in the `/tl` response is the one everything afterwards uses. Presenting a stale `ct` is the most common way a correct payload still fails.
{% endhint %}

## The flow, end to end

1. **Request** a protected page or API and get blocked.
2. **`GET /mfc`** on the sensor host. Keep the `x-kpsdk-h` it responds with.
3. **`GET /fp?x-kpsdk-v=...`** - the challenge page. It returns the first `x-kpsdk-ct` in a header and the interrogation manifest inside a `postMessage('KPSDK:MC:<im>:...')`.
4. **`GET /ips.js?KP_UIDz=<ct>&x-kpsdk-v=<v>&x-kpsdk-im=<im>`** - the interrogation script, keyed to this session.
5. **Solve** - send the script and its link to `POST /tl` and get the headers and payload back.
6. **`POST /tl`** on the sensor host, body `application/octet-stream`. Keep the rotated `x-kpsdk-ct` and the `x-kpsdk-st` from the response.
7. **Solve the proof of work** - send `st`, the rotated `ct` and the site's domain to `POST /cd`.
8. **Request the protected endpoint** carrying `x-kpsdk-ct`, `x-kpsdk-h`, `x-kpsdk-v` and `x-kpsdk-cd`.

Steps 1-4 and 6-8 happen entirely on your side, from your proxy, in your cookie jar. Steps 5 and 7 are the only calls to us, and only step 5 is billed.

{% hint style="info" %}
Steps 2-4 are an ordinary browser handshake and you must perform them yourself - the script served at step 4 is bound to the cookies and tokens the first three established. Handing us a script fetched by anything other than the session you will spend it on produces a payload that is refused.
{% endhint %}

### The script has to be fresh

The script's decode seed is derived from the wall clock in roughly five-hour buckets, and the search window reaches about four days either side. A script captured today solves all day. One from last week fails every solve on the checksum and comes back as a `502`. If a target that has been working starts returning `502`, fetch a new `ips.js` before looking for anything else.

## Billing

**A solve is one `POST /tl`.** That is the billable unit, and it is what the per-solve rate on your plan refers to.

**`POST /cd` is free and unmetered on every plan.** It is a hash search against a salt we already hold, and a caller needs one blob per protected request - billing it would charge several times over for one session. It is still logged, so your proof-of-work volume shows up on the dashboard; it is simply never charged.

Refused requests are not billed. A `400`, `401`, `403` or `502` costs nothing.

## Whitelisting

Kasada targets are approved per host. Until the host you are solving for is on your account's whitelist, `POST /tl` and `POST /cd` answer `403` and the message names the host that was refused:

```json
{
  "success": false,
  "error": {
    "type": "validation",
    "message": "site \"k.example.net\" is not in the whitelist"
  }
}
```

A **site here is a host**, not a registrable domain. For `/tl` the host is read off the `ips_link` you send; for `/cd` it is the `domain` field.

{% hint style="warning" %}
**One property can need two rows.** The host serving `ips.js` and the host the proof-of-work salt is filed under are not always the same. Twitch serves the script from `k.twitchcdn.net`, runs the page on `www.twitch.tv`, and files its salt under the protected API, `gql.twitch.tv` - so an account solving Twitch end to end needs both `k.twitchcdn.net` and `gql.twitch.tv` approved. A whitelist covering only the script host gives you a payload and then a `403` on the proof of work.
{% endhint %}

Request a target through the dashboard, and list both hosts if the script host and the API host differ.

## Next

* [Payload](/kasada/payload.md) - the `POST /tl` guide, start to finish.
* [Proof of work](/kasada/proof-of-work.md) - the `POST /cd` guide.
* [API Reference](/kasada/api-reference.md) - every field and every status code.
