| Time | Status | User Agent | |
|---|---|---|---|
Retrieving recent requests… | |||
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).
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 Forward the request (with mapped upstream headers) to your backend server. |
| Challenge | Anything other than 200 | Challenge the request 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 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
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:
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: 1The module's execution:
- To your Application: Add the header
X-DataDome-isbot: 1. - To the User: Add the headers
Set-Cookie: datadome=ah78andX-DD-B: 1. - Here is an example of response headers sent by the API:
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: 403Functional requirements
Identifying the ClientID
The ClientIDis 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.
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-ClientIDheader, its value should be picked for theClientIDfield instead of thedatadomecookie.
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: trueto 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.
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
endSequential diagram of the full flow
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, SecCHUAFullVersionList, Via | 256 |
| Accept, ClientID, HeadersList, Host, Origin, ServerHostname, ServerName, Signature, SignatureAgent, XForwardedForIP* | 512 |
| UserAgent | 768 |
| CookiesList, Referer | 1024 |
| Request, SignatureInput | 2048 |

