> ## Documentation Index
> Fetch the complete documentation index at: https://docs.repdata.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Predupe Integration

> Call the Research Defender PREDUPE API with a POST request, authorized with your key in the x-api-key header.

## Overview

The PREDUPE API checks whether a respondent has already attempted surveys on a destination platform. You send the destination platform and the survey numbers to check, and the response reports, per survey, whether a duplicate was found. This guide shows how to send the request from the browser with `POST`, passing the parameters as a JSON body.

Like the Activity API, PREDUPE is authorized with an **authorization key** that you send in the `x-api-key` header. It runs on `search.resdefio.com`.

<Note>
  Use `POST` from the browser. Browsers do not send a request body with `GET`.
</Note>

## Authentication

Send your authorization key in the `x-api-key` header of every request.

<ParamField header="x-api-key" type="string" required>
  Your Research Defender authorization key.
</ParamField>

<Warning>
  Your authorization key is a secret. The sample on this page calls the API from the browser for demonstration, but anything in client-side code can be read. If you can, call the PREDUPE API from your server and keep the key there.
</Warning>

## Endpoint

<ParamField path="Endpoint" type="string">
  `POST https://{environment}.search.resdefio.com/predupes`
</ParamField>

| Environment | Endpoint |
| - | - |
| `prod` | `https://prod.search.resdefio.com/predupes` |
| `stage` | `https://stage.search.resdefio.com/predupes` |

In the sample, set `ENVIRONMENT` to `"prod"` or `"stage"`.

## Request body

Send the parameters as a JSON object.

<ParamField body="Destination" type="array" required>
  The destination platforms to check. Each entry has a `Platform_Id` and its `Survey_Numbers`.
</ParamField>

<ParamField body="Destination[].Platform_Id" type="string" required>
  The destination key for the client to check the traffic with. Find it with the [DESTINATION\_PLATFORM](/research-defender/endpoints/get-api-v3-respondents-destination_platforms-private_key) lookup.
</ParamField>

<ParamField body="Destination[].Survey_Numbers" type="array" required>
  One or more survey numbers on that platform. A request supports 30 survey numbers in total; beyond that, entries are processed randomly.
</ParamField>

<ParamField body="Session_Id" type="string">
  The respondent's session ID. Optional.
</ParamField>

<ParamField body="Panel_Id" type="string">
  The panel ID. Optional.
</ParamField>

<ParamField body="Hashed_Email" type="string">
  The respondent's hashed email address. Optional.
</ParamField>

<ParamField body="Supplier_Id" type="string">
  The supplier ID, for reporting only. Optional.
</ParamField>

### Sending more parameters

The sample sends `Destination` and `Session_Id`. To send `Panel_Id`, `Hashed_Email`, or `Supplier_Id`, add them to the object in `buildPayload()`. A request body with every parameter looks like this:

```json theme={null}
{
  "Destination": [
    {
      "Platform_Id": "YOUR_DESTINATION_PLATFORM_ID",
      "Survey_Numbers": ["YOUR_SURVEY_NUMBER_1", "YOUR_SURVEY_NUMBER_2"]
    }
  ],
  "Session_Id": "YOUR_SESSION_ID",
  "Panel_Id": "YOUR_PANEL_ID",
  "Hashed_Email": "YOUR_HASHED_EMAIL",
  "Supplier_Id": "YOUR_SUPPLIER_ID"
}
```

## Response

Each survey in `Destination[].Surveys` has a `Flag` (`1` if a duplicate was found, `0` if not), `Duplicate_Initial_UD`, `Survey_Number`, `Duplicate_Score` (`0` or `100`), and `Duplicate_Potential` (`low` or `high`). The response also has a top-level `Threat_Potential_Score`. See [PREDUPE](/research-defender/guides/predupe/01-overview#response-parameters) for the field definitions.

```json theme={null}
{
  "Destination": [
    {
      "Platform_Name": "Client Account 1",
      "Surveys": [
        {
          "Flag": 1,
          "Survey_Number": "TEST_20260819_03",
          "Duplicate_Initial_UD": "T__TEST_20260819_03_01",
          "Duplicate_Potential": "high",
          "Duplicate_Score": 100
        },
        {
          "Flag": 0,
          "Survey_Number": "TEST_20260819_04",
          "Duplicate_Initial_UD": "",
          "Duplicate_Potential": "low",
          "Duplicate_Score": 0
        }
      ]
    }
  ],
  "Threat_Potential_Score": 22
}
```

## Example

<Tabs>
  <Tab title="JavaScript">
    ```javascript predupe-post.js theme={null}
    // ---------------------------------------------------------------
    // Configuration
    // ---------------------------------------------------------------
    const ENVIRONMENT = "prod"; // "stage" or "prod"

    // Your authorization key, sent in the x-api-key header. Keep it secret.
    // This sample calls the API from the browser for demonstration only. Anything
    // in client-side code can be read, so call the API from your server if you can.
    const API_KEY = "YOUR_API_KEY";

    // The destination platform and the survey numbers to check on it (30 in total, at most).
    const PLATFORM_ID = "YOUR_DESTINATION_PLATFORM_ID"; // Destination[].Platform_Id
    const SURVEY_NUMBERS = ["YOUR_SURVEY_NUMBER_1", "YOUR_SURVEY_NUMBER_2"]; // Destination[].Survey_Numbers

    // Optional. Set a value to "" to leave it out of the request.
    const SESSION_ID = "YOUR_SESSION_ID"; // Session_Id (optional)

    const PREDUPE_ENDPOINT = `https://${ENVIRONMENT}.search.resdefio.com/predupes`;

    // ---------------------------------------------------------------
    // Helpers
    // ---------------------------------------------------------------

    /** Builds the request body. Empty optional values are left out. */
    function buildPayload() {
      const payload = {
        Destination: [{ Platform_Id: PLATFORM_ID, Survey_Numbers: SURVEY_NUMBERS }],
        Session_Id: SESSION_ID,
      };

      return Object.fromEntries(
        Object.entries(payload).filter(([, value]) => value !== "" && value != null)
      );
    }

    /** Parses a response body as JSON, or returns the raw text if it is not JSON. */
    function parseResponse(text) {
      try {
        return JSON.parse(text);
      } catch {
        return text;
      }
    }

    /** Sends the POST request and returns the parsed result. */
    async function checkPredupes(payload) {
      const response = await fetch(PREDUPE_ENDPOINT, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          "x-api-key": API_KEY, // authorization key
        },
        body: JSON.stringify(payload),
      });

      const text = await response.text();

      if (!response.ok) {
        throw new Error(`PREDUPE request failed with status ${response.status}: ${text.slice(0, 300)}`);
      }

      return parseResponse(text);
    }

    // ---------------------------------------------------------------
    // Usage
    // ---------------------------------------------------------------
    const result = await checkPredupes(buildPayload());

    for (const destination of result.Destination ?? []) {
      for (const survey of destination.Surveys ?? []) {
        console.log(survey.Survey_Number, "Flag:", survey.Flag, "Score:", survey.Duplicate_Score);
      }
    }
    console.log("Threat Potential Score:", result.Threat_Potential_Score);
    ```
  </Tab>

  <Tab title="cURL">
    ```bash theme={null}
    curl -X POST "https://prod.search.resdefio.com/predupes" \
      -H "Content-Type: application/json" \
      -H "x-api-key: YOUR_API_KEY" \
      -d '{
        "Destination": [
          {
            "Platform_Id": "YOUR_DESTINATION_PLATFORM_ID",
            "Survey_Numbers": ["YOUR_SURVEY_NUMBER_1", "YOUR_SURVEY_NUMBER_2"]
          }
        ],
        "Session_Id": "YOUR_SESSION_ID"
      }'
    ```
  </Tab>
</Tabs>

The JavaScript example builds the body with `buildPayload()`, which leaves out empty optional values. `checkPredupes()` sends the request with the `Content-Type` and `x-api-key` headers and shows the API's message if the request fails.

## Full HTML page

If you want a complete page you can open in a browser, use the sample below. Replace the placeholder values at the top of the script.

<Accordion title="Predupe integration with POST (HTML)">
  ```html predupe-post-integration.html theme={null}
  <!DOCTYPE html>
  <html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Research Defender – Predupe Integration (POST)</title>
  </head>
  <body>
    <h1>Research Defender – Predupe Integration (POST)</h1>
    <pre id="output">Loading…</pre>

    <script>
      // ---------------------------------------------------------------
      // Configuration
      // ---------------------------------------------------------------
      const ENVIRONMENT = "prod"; // "stage" or "prod"

      // Your authorization key, sent in the x-api-key header. Keep it secret.
      // This sample calls the API from the browser for demonstration only. Anything
      // in client-side code can be read, so call the API from your server if you can.
      const API_KEY = "YOUR_API_KEY";

      // The destination platform and the survey numbers to check on it (30 in total, at most).
      const PLATFORM_ID = "YOUR_DESTINATION_PLATFORM_ID"; // Destination[].Platform_Id
      const SURVEY_NUMBERS = ["YOUR_SURVEY_NUMBER_1", "YOUR_SURVEY_NUMBER_2"]; // Destination[].Survey_Numbers

      // Optional. Set a value to "" to leave it out of the request.
      const SESSION_ID = "YOUR_SESSION_ID"; // Session_Id (optional)

      const PREDUPE_ENDPOINT = `https://${ENVIRONMENT}.search.resdefio.com/predupes`;

      // ---------------------------------------------------------------
      // Helpers
      // ---------------------------------------------------------------

      /** Builds the request body. Empty optional values are left out. */
      function buildPayload() {
        const payload = {
          Destination: [{ Platform_Id: PLATFORM_ID, Survey_Numbers: SURVEY_NUMBERS }],
          Session_Id: SESSION_ID,
        };

        return Object.fromEntries(
          Object.entries(payload).filter(([, value]) => value !== "" && value != null)
        );
      }

      /** Parses a response body as JSON, or returns the raw text if it is not JSON. */
      function parseResponse(text) {
        try {
          return JSON.parse(text);
        } catch {
          return text;
        }
      }

      /** Sends the POST request and returns the parsed result. */
      async function checkPredupes(payload) {
        const response = await fetch(PREDUPE_ENDPOINT, {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
            "x-api-key": API_KEY, // authorization key
          },
          body: JSON.stringify(payload),
        });

        const text = await response.text();

        if (!response.ok) {
          throw new Error(`PREDUPE request failed with status ${response.status}: ${text.slice(0, 300)}`);
        }

        return parseResponse(text);
      }

      // ---------------------------------------------------------------
      // Run
      // ---------------------------------------------------------------
      window.addEventListener("load", async () => {
        const output = document.getElementById("output");

        try {
          const result = await checkPredupes(buildPayload());

          console.log("PREDUPE result:", result);
          output.textContent =
            typeof result === "string" ? result : JSON.stringify(result, null, 2);
        } catch (error) {
          console.error(error);
          output.textContent = `Error: ${error.message}`;
        }
      });
    </script>
  </body>
  </html>
  ```
</Accordion>

## Troubleshooting

| Symptom | Likely cause |
| - | - |
| The request is rejected with an authorization error | The `x-api-key` header is missing or the key is wrong. |
| The request returns an error about the destination | The `Platform_Id` is wrong. Look it up with the DESTINATION\_PLATFORM lookup. |
| Some survey numbers are not in the response | A request processes 30 survey numbers in total. Send fewer survey numbers per request. |
| The request is blocked in the browser | The browser could not complete the request to `search.resdefio.com`. Check the browser console, and consider calling the API from your server. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.