---
updatedAt: 2026-08-24T15:51:22.000Z
---

Fetch the complete documentation index at: https://docs.datadome.co/llms.txt. Use this file to discover all available pages before exploring further. Append .md to any documentation page URL to get its markdown version.

# Protection API

The Validate Request API checks incoming traffic against DataDome'sbot detection engine. Your integration sends request metadata to DataDome, and we return a decision: allow or challenge.

DataDome API should be requested by a component of your backend infrastructure (CDN, load balancer, application server, etc).

<Callout icon="🚧" theme="warn">
  ### Protection API (custom integration) is only available for Premium and Enterprise customer.
</Callout>

## Pre-requisites to implement API Integration

### External API Communication

* The component must support synchronous HTTPS calls to external APIs.

### HTTP Request Analysis

* Ability to read and process all HTTP request headers.
* Ability to access the end user’s IP address.
* \[Recommended] Ability to collect TLS JA3 and JA4 fingerprints from incoming requests.

### Request Management

* Configurable HTTP request timeout with a fail-open mechanism to maintain service continuity.
* Ability to exclude static assets (e.g., .css, .js, .jpg, etc.) from DataDome processing.

### Response Handling

* Ability to dynamically inject custom headers into HTTP responses (e.g., `Set-Cookie: datadome=xxxxxxxx`).
* Ability to store and forward data from the DataDome HTTP response to the final client response.
* Ability to return custom HTTP responses (html or json) with specific status codes (e.g., 401, 403, etc.) based on DataDome feedback.

## Handling API response status code and body

The DataDome response is dynamic. It tells the integration what decision to enforce and how to update the request/response headers.

### 1.Response Logic

Once the response is validated, the module must behave based on the value of `X-DataDomeResponse` header returned:

| Action    | Status Codes            | Logic to Follow                                                                                                                                                                 |
| :-------- | :---------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Allow     | 200                     | **Pass the request**<br />Forward the request (with mapped upstream headers) to your backend server.                                                                            |
| Challenge | Anything other than 200 | **Challenge the request**<br />Immediately return the DataDome API response (status code + body + mapped downstream headers) to the client. Do not contact your backend server. |
| Fail-open | Header missing          | **Ignore it**<br />Treat the request as allowed (200) to avoid blocking legitimate traffic due to system errors.                                                                |

### 2. Handling API response headers

The DataDome API response contains instructions on how to modify both the upstream request (sent to your origin) and the downstream response (sent to the user). This must be done for both Allow and challenge status codes.

### Universal Header Mapping

The API uses "Pointer Headers" to tell your module which specific headers to extract and where to move them. It is Mandatory for allow and challenge status codes.

| Pointer Header             | Purpose        | Action Required                                                                           |
| :------------------------- | :------------- | :---------------------------------------------------------------------------------------- |
| X-DataDome-request-headers | Upstream Map   | Lists headers in the API response that you must add to the request reaching your backend. |
| X-DataDome-headers         | Downstream Map | Lists headers in the API response that you must add to the response reaching the browser. |

**Security Guardrail**: Never forward the "Pointer Headers" themselves (or any header listed inside them) back to the end-user. They are for your module's internal logic only.

#### Implementation Workflow

To process headers correctly, follow these two steps:

##### Step A: Update the Request (To Origin)

Check the value of `X-DataDome-request-headers`. It contains a space-separated list of header names. Extract these from the API response and inject them into the request before it hits your application. This feature is called [Enriched headers](https://docs.datadome.co/docs/logs-integration)

#### Step B: Update the Response (To User)

Check the value of X-DataDome-headers. Extract these from the API response and add them to the final response sent to the browser.

#### Practical Example

If the DataDome API response looks like this:

```Text HTTP Headers returned by DataDome API
X-DataDome-request-headers: X-DataDome-isbot
X-DataDome-isbot: 1
X-DataDome-headers: Set-Cookie X-DD-B
Set-Cookie: datadome=ah78
X-DD-B: 1
```

#### The module's execution:

* To your Application: Add the header `X-DataDome-isbot: 1`.
* To the User: Add the headers` Set-Cookie: datadome=ah78` and `X-DD-B: 1`.
* **Here is an example of response headers sent by the API:**

```http API response
X-DataDome-request-headers: X-DataDome-botname X-DataDome-botfamily X-DataDome-isbot
X-DataDome-botname: Crawler fake Google
X-DataDome-botfamily: bad_bot
X-DataDome-isbot: 1
X-DataDome-headers: Set-Cookie Pragma X-DataDome Cache-Control
Set-Cookie: datadome=some-value; Domain=domain.com; Path=/; Expires=Wed, 13 Jan 2021 22:23:01 GMT;
Pragma: no-cache
X-DataDome: protected
Cache-Control: no-cache
X-DataDomeResponse: 403
```

## Functional requirements

### Identifying the ClientID

The `ClientID`is a mandatory field for the Protection API. It helps us to track the user session. Its source varies depending on whether the user is a standard web visitor or a client using [Session by Header](https://docs.datadome.co/docs/how-to-configure-the-javascript-tag#sessionbyheader).

#### Extraction Logic

The module must check for the presence of the `X-DataDome-ClientID` header before falling back to the standard cookie.

| Source Priority | Location                     | Condition                                          |
| :-------------- | :--------------------------- | :------------------------------------------------- |
| Priority 1      | `X-DataDome-ClientID` Header | If this header is present in the incoming request. |
| Priority 2      | datadome Cookie value        | If the custom header above is missing.             |

* If an incoming HTTP request includes a `X-DataDome-ClientID` header, its value should be picked for the `ClientID` field instead of the `datadome` cookie.

#### Implementation Requirements

When you extract the ID from the Priority 1 (Header) source, you must also signal to DataDome that you are expecting a custom cookie response format:

* Request Field: Map the extracted value to the ClientID field in your API payload.
* Response Signal: You must add the header `X-DataDome-X-Set-Cookie: true` to the request sent to DataDome's Protection API.

Expected Result: DataDome will return the new cookie value in the `X-Set-Cookie` header instead of the standard Set-Cookie (You don't need to handle this case. It will be handled out of the box though headers management).

#### Implementation Example (Lua)

This logic ensures the correct ID is sent and the system knows how to handle the session update.

```lua Lua
if request_headers['x-datadome-clientid'] ~= nil then  
   body['ClientID'] = request_headers['x-datadome-clientid']  
   datadomeHeaders["X-DataDome-X-Set-Cookie"] = "true"  
else  
   body['ClientID'] = clientId  
end
```

## Sequential diagram of the full flow

```mermaid
sequenceDiagram
    participant Client
    participant Module as Integration Module
    participant API as DataDome API
    participant Origin as Origin Server

    Client->>Module: 1. Incoming Request

    Note right of Module: Step 1: Extract ClientID<br/>Priority: Custom Header > Cookie

    Module->>API: 2. POST /validate
    activate API
    API-->>Module: API Response (Headers + JSON Body)
    deactivate API

    alt Decision: Fail-open (X-DataDomeResponse missing)
        Note right of Module: Action: Ignore API<br/>Treat request as allowed (200)<br/>to avoid blocking legitimate traffic.
        Module->>Origin: Forward Original Request
        activate Origin
        Origin-->>Module: Origin Response
        deactivate Origin
        Module-->>Client: Return Origin Response
    else Header Present (Proceed to Logic)
        Note over Module: Step 2: Universal Header Mapping<br/>Identify which headers to extract from the API response:<br/>1. Read 'X-DataDome-request-headers' -> Extract these for Upstream (Origin)<br/>2. Read 'X-DataDome-headers' -> Extract these for Downstream (Client)

        alt Decision: Challenge (X-DataDomeResponse != 200)
            Note right of Module: Action: Challenge<br/>1. Set HTTP Status Code to the value of X-DataDomeResponse<br/>2. Do NOT contact backend server<br/>3. Inject mapped Downstream Headers
            Module-->>Client: Return Status (from API X-DataDomeResponse) + API Body + Downstream Headers
        else Decision: Allow (X-DataDomeResponse == 200)
            Note right of Module: Action: Allow<br/>Inject mapped Upstream Headers<br/>(e.g., X-DataDome-isbot)
            Module->>Origin: Forward Request + Upstream Headers
            activate Origin
            Origin-->>Module: Origin Response
            deactivate Origin
            Note right of Module: Inject mapped Downstream Headers<br/>(e.g., Set-Cookie, X-DD-B)
            Module-->>Client: Return Origin Response + Downstream Headers
        end
    end                                                 
```

## Payload Formatting & Constraints

* **Encoding**: All values must be URL encoded.
* **Header Handling**: Exclude fields for empty headers.
* **Size & Truncation**: To stay within the global 24 kB limit, truncate fields as listed below.
* **Reverse truncation** : Fields with an asterisk (\*) are truncated from the end.

| Fields                                                                                                                                                                           | Limit per field (in bytes) |
| :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------- |
| Key, APIConnectionState, AuthorizationLen, CookiesLen, IP, Method, ModuleVersion, Port, PostParamLen, Protocol, RequestModuleName, TimeRequest                                   | Unlimited                  |
| JsonRpcVersion, SecCHDeviceMemory, SecCHUAMobile, SecFetchStorageAccess, SecFetchUser                                                                                            | 8                          |
| McpParamsClientInfoVersion, McpProtocolVersion, SecCHUAArch                                                                                                                      | 16                         |
| SecCHUAPlatform, SecFetchDest, SecFetchMode                                                                                                                                      | 32                         |
| ContentType, JsonRpcRequestId, McpMethod, McpParamsClientInfoName, McpParamsToolName, McpSessionId, SecFetchSite                                                                 | 64                         |
| AcceptCharset, AcceptEncoding, CacheControl, Connection, From, GraphQLOperationName, Pragma, SecCHUA, SecCHUAModel, TrueClientIP, UserID, X-Real-IP, X-Requested-With, ProductId | 128                        |
| AcceptLanguage, CustomFieldString1, CustomFieldString2, CustomFieldString3, SecCHUAFullVersionList, Via                                                                          | 256                        |
| Accept, ClientID, HeadersList, Host, Origin, ServerHostname, ServerName, Signature, SignatureAgent, XForwardedForIP\*                                                            | 512                        |
| UserAgent                                                                                                                                                                        | 768                        |
| CookiesList, Referer                                                                                                                                                             | 1024                       |
| Request, SignatureInput                                                                                                                                                          | 2048                       |

# OpenAPI definition

```json
{
  "openapi": "3.1.0",
  "info": {
    "title": "protection-api",
    "version": "6.0"
  },
  "servers": [
    {
      "url": "https://api.datadome.co"
    }
  ],
  "components": {
    "securitySchemes": {
      "sec0": {
        "type": "apiKey",
        "in": "query",
        "name": "api_key"
      }
    }
  },
  "security": [
    {
      "sec0": []
    }
  ],
  "paths": {
    "/validate-request/": {
      "post": {
        "summary": "Protection API",
        "description": "",
        "operationId": "validate-request",
        "parameters": [
          {
            "name": "Content-Type",
            "in": "header",
            "description": "Should be `application/x-www-form-urlencoded`",
            "required": true,
            "schema": {
              "type": "string",
              "default": "application/x-www-form-urlencoded"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": [
                  "Key",
                  "Accept",
                  "AcceptCharset",
                  "AcceptEncoding",
                  "AcceptLanguage",
                  "AuthorizationLen",
                  "CacheControl",
                  "ClientID",
                  "ContentType",
                  "CookiesLen",
                  "CookiesList",
                  "From",
                  "HeadersList",
                  "Host",
                  "IP",
                  "Method",
                  "ModuleVersion",
                  "Origin",
                  "Port",
                  "PostParamLen",
                  "Pragma",
                  "Protocol",
                  "Referer",
                  "Request",
                  "RequestModuleName",
                  "ServerHostname",
                  "ServerName",
                  "TimeRequest",
                  "TrueClientIP",
                  "UserAgent",
                  "Via",
                  "XForwardedForIP",
                  "X-Requested-With",
                  "X-Real-IP"
                ],
                "properties": {
                  "Key": {
                    "type": "string",
                    "description": "The server-side API key provided on your dashboard"
                  },
                  "Accept": {
                    "type": "string",
                    "description": "The value of the `Accept` request header",
                    "default": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"
                  },
                  "AcceptCharset": {
                    "type": "string",
                    "description": "The value of the `Accept-Charset` request header"
                  },
                  "AcceptEncoding": {
                    "type": "string",
                    "description": "The value of the `Accept-Encoding` request header",
                    "default": "gzip, deflate, sdch"
                  },
                  "AcceptLanguage": {
                    "type": "string",
                    "description": "The value of the `Accept-Language` request header",
                    "default": "fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4"
                  },
                  "APIConnectionState": {
                    "type": "string",
                    "description": "`new` if the API request is using a new connection, or `reuse` if it reused a keep-alive connection",
                    "default": "new"
                  },
                  "AuthorizationLen": {
                    "type": "integer",
                    "description": "The length of the `Authorization` request header's value",
                    "format": "int32"
                  },
                  "CacheControl": {
                    "type": "string",
                    "description": "The value of the `Cache-Control` request header"
                  },
                  "ClientID": {
                    "type": "string",
                    "description": "The value of the `datadome` cookie attached to the client request"
                  },
                  "Connection": {
                    "type": "string",
                    "description": "The value of the `Connection` request header",
                    "default": "keep-alive"
                  },
                  "ContentType": {
                    "type": "string",
                    "description": "The value of the `Content-Type` request header"
                  },
                  "CookiesLen": {
                    "type": "integer",
                    "description": "The length of the `Cookie` request header's value (including all cookies)",
                    "format": "int32"
                  },
                  "CookiesList": {
                    "type": "string",
                    "description": "A comma-separated list of all cookies' keys coming from the client request, with their original order preserved"
                  },
                  "CustomFieldFloat1": {
                    "type": "number",
                    "description": "User-defined float value",
                    "format": "float"
                  },
                  "CustomFieldInteger1": {
                    "type": "integer",
                    "description": "User-defined integer value",
                    "format": "int32"
                  },
                  "CustomFieldInteger2": {
                    "type": "integer",
                    "description": "User-defined integer value",
                    "format": "int32"
                  },
                  "CustomFieldString1": {
                    "type": "string",
                    "description": "User-defined string value"
                  },
                  "CustomFieldString2": {
                    "type": "string",
                    "description": "User-defined string value"
                  },
                  "CustomFieldString3": {
                    "type": "string",
                    "description": "User-defined string value"
                  },
                  "From": {
                    "type": "string",
                    "description": "The value of the `From` request header"
                  },
                  "GraphQLOperationCount": {
                    "type": "integer",
                    "description": "Number of GraphQL operation in the request",
                    "format": "int32"
                  },
                  "GraphQLOperationName": {
                    "type": "string",
                    "description": "Name of the GraphQL operation"
                  },
                  "GraphQLOperationType": {
                    "type": "string",
                    "description": "Type of the GraphQL operation (query/mutation/subscription)"
                  },
                  "HeadersList": {
                    "type": "string",
                    "description": "A comma-separated list of all headers coming from the client request, with their original order preserved",
                    "default": "Host,Connection,Pragma,Cookie,Cache-Control,User-Agent"
                  },
                  "Host": {
                    "type": "string",
                    "description": "The value of the `Host` request header",
                    "default": "sub.domain.com"
                  },
                  "IP": {
                    "type": "string",
                    "description": "The IP address (v4 or v6) of the client that sent the request",
                    "default": "62.35.12.13"
                  },
                  "JA3": {
                    "type": "string",
                    "description": "Value of JA3 TLS Fingerprinting coming from an implementation of https://github.com/salesforce/ja3",
                    "default": "e7d705a3286e19ea42f587b344ee6865"
                  },
                  "JA4": {
                    "type": "string",
                    "description": "Value of JA4 TLS Fingerprinting coming from an implementation of https://github.com/FoxIO-LLC/ja4",
                    "default": "t13d1516h2_8daaf6152771_02713d6af862"
                  },
                  "JsonRpcRequestId": {
                    "type": "string",
                    "description": "The ID of the JSON-RPC request"
                  },
                  "JsonRpcVersion": {
                    "type": "string",
                    "description": "The version of the JSON-RPC protocol"
                  },
                  "McpMethod": {
                    "type": "string",
                    "description": "The value of the Model Context Protocol method"
                  },
                  "McpParamsClientInfoName": {
                    "type": "string",
                    "description": "Name of the Model Context Protocol client implementation used in the `initialize` method"
                  },
                  "McpParamsClientInfoVersion": {
                    "type": "string",
                    "description": "Version of the Model Context Protocol client implementation used in the `initialize` method"
                  },
                  "McpParamsToolName": {
                    "type": "string",
                    "description": "Name of the tool invoked in the `tools/call` method"
                  },
                  "McpProtocolVersion": {
                    "type": "string",
                    "description": "The value of the `Mcp-Protocol-Version` request header"
                  },
                  "McpSessionId": {
                    "type": "string",
                    "description": "The value of the `Mcp-Session-Id` request header"
                  },
                  "Method": {
                    "type": "string",
                    "description": "The method name of the request (GET, POST, OPTIONS, etc.)",
                    "default": "GET"
                  },
                  "ModuleVersion": {
                    "type": "string",
                    "description": "The version number of the module that is processing the request",
                    "default": "1.0"
                  },
                  "Origin": {
                    "type": "string",
                    "description": "The value of the `Origin` request header"
                  },
                  "Port": {
                    "type": "integer",
                    "description": "The port number of the TCP/IP connection that issued the request (from the client)",
                    "default": 60200,
                    "format": "int32"
                  },
                  "PostParamLen": {
                    "type": "integer",
                    "description": "The length of a POST request payload reported by the `Content-Length` header",
                    "format": "int32"
                  },
                  "Pragma": {
                    "type": "string",
                    "description": "The value of the `Pragma` request header",
                    "default": "no-cache"
                  },
                  "ProductId": {
                    "type": "string",
                    "description": "The value of the product Id being seen or added to the customer basket"
                  },
                  "Protocol": {
                    "type": "string",
                    "description": "The protocol scheme used on the request's URL",
                    "default": "https"
                  },
                  "Referer": {
                    "type": "string",
                    "description": "The value of the `Referer` request header",
                    "default": "http://sub.domain.com/home.php"
                  },
                  "Request": {
                    "type": "string",
                    "description": "The path and query parts of the request's URL",
                    "default": "/folder/file.php?param=value"
                  },
                  "RequestModuleName": {
                    "type": "string",
                    "description": "The name of the module that is processing the request",
                    "default": "my_connector"
                  },
                  "SecCHDeviceMemory": {
                    "type": "string",
                    "description": "The value of the `Sec-CH-Device-Memory` request header"
                  },
                  "SecCHUA": {
                    "type": "string",
                    "description": "The value of the `Sec-CH-UA` request header"
                  },
                  "SecCHUAArch": {
                    "type": "string",
                    "description": "The value of the `Sec-CH-UA-Arch` request header"
                  },
                  "SecCHUAFullVersionList": {
                    "type": "string",
                    "description": "The value of the `Sec-CH-UA-Full-Version-List` request header"
                  },
                  "SecCHUAMobile": {
                    "type": "string",
                    "description": "The value of the `Sec-CH-UA-Mobile` request header"
                  },
                  "SecCHUAModel": {
                    "type": "string",
                    "description": "The value of the `Sec-CH-UA-Model` request header"
                  },
                  "SecCHUAPlatform": {
                    "type": "string",
                    "description": "The value of the `Sec-CH-UA-Platform` request header"
                  },
                  "SecFetchDest": {
                    "type": "string",
                    "description": "The value of the `Sec-Fetch-Dest` request header"
                  },
                  "SecFetchMode": {
                    "type": "string",
                    "description": "The value of the `Sec-Fetch-Mode` request header"
                  },
                  "SecFetchSite": {
                    "type": "string",
                    "description": "The value of the `Sec-Fetch-Site` request header"
                  },
                  "SecFetchStorageAccess": {
                    "type": "string",
                    "description": "The value of the `Sec-Fetch-Storage-Access` request header"
                  },
                  "SecFetchUser": {
                    "type": "string",
                    "description": "The value of the `Sec-Fetch-User` request header"
                  },
                  "ServerHostname": {
                    "type": "string",
                    "description": "The virtual hostname of the server that is processing the request",
                    "default": "sub.domain.com"
                  },
                  "ServerName": {
                    "type": "string",
                    "description": "The name of the server which is processing the request",
                    "default": "haproxy001"
                  },
                  "ServerRegion": {
                    "type": "string",
                    "description": "The region of the server which is processing the request"
                  },
                  "Signature": {
                    "type": "string",
                    "description": "The value of the `Signature` request header"
                  },
                  "SignatureAgent": {
                    "type": "string",
                    "description": "The value of the `Signature-Agent` request header"
                  },
                  "SignatureInput": {
                    "type": "string",
                    "description": "The value of the `Signature-Input` request header"
                  },
                  "SkyfirePayId": {
                    "type": "string",
                    "description": "The value of the `Skyfire-Pay-Id` request header for AI bot monetization"
                  },
                  "TimeRequest": {
                    "type": "integer",
                    "description": "A timestamp in microseconds marking the time at which the request was processed by the module",
                    "default": 1494584456492817,
                    "format": "int64"
                  },
                  "TlsCipher": {
                    "type": "string",
                    "description": "Hexadecimal value representing all cipher suites that are available, in order of preference",
                    "default": "130113021303C02FC02BC030C02CCCA9CCA8C009C013C00AC014009C009D002F0035000A"
                  },
                  "TlsProtocol": {
                    "type": "string",
                    "description": "TlsProtocol",
                    "default": "TLSv1.3"
                  },
                  "TrueClientIP": {
                    "type": "string",
                    "description": "The value of the `True-Client-IP` request header"
                  },
                  "UserAgent": {
                    "type": "string",
                    "description": "The value of the `User-Agent` request header",
                    "default": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.96 Safari/537.36"
                  },
                  "UserID": {
                    "type": "string",
                    "description": "Internal customer or client ID (apply only when your customer is log in)"
                  },
                  "Via": {
                    "type": "string",
                    "description": "The value of the `Via` request header"
                  },
                  "XForwardedForIP": {
                    "type": "string",
                    "description": "The value of the `X-Forwarded-For` request header",
                    "default": "62.35.64.32,32.36.35.24"
                  },
                  "X-Requested-With": {
                    "type": "string",
                    "description": "The value of the `X-Requested-With` request header"
                  },
                  "X-Real-IP": {
                    "type": "string",
                    "description": "The value of the `X-Real-IP` request header"
                  }
                }
              }
            }
          }
        },
        "security": []
      }
    }
  },
  "x-readme": {
    "headers": [],
    "explorer-enabled": true,
    "proxy-enabled": true
  },
  "x-readme-fauxas": true
}
```

# Sibling pages

* [HealthCheck](https://docs.datadome.co/reference/check.md)