Skip to main content

React Toolbox Integration

This guide will walk you through integrating the DQCToolBox with React websites.

Integration Flow

  1. You need a surveyId
    1. This is an identifier for your survey or questionnaire.
    2. It can be any string that helps you identify the survey (e.g., "Customer Satisfaction Survey", "Survey123", etc.).
  2. Call the getDQCRequestId(surveyId) function to get a requestId.
    1. Make sure to call this function only once per survey per user.
    2. Be careful with multiple calls as it will generate multiple requestIds. This can affect the duplicate check.
    3. If a user takes the same survey multiple times, you can call it again to get a new requestId. You can expect a isDuplicate: true for the same participantID and the same surveyId.
  3. Store your requestId in your database or as a new question in your survey.
    • For fast integration, you only need to save this requestId. Data will be available later in your DQCO-OP dashboard when sending the transaction data with the requestId.
    • If you need to get all the DQC data immediately, you can call the next function after getting the requestId.
  4. (Optional) Call the getDQCData(requestId) function to get the tools DQC data.
    • You can call our API with your requestId and API key to get the tools DQC data at any time.
    • This method will provide you the tools DQC data for the given requestId. The data returned has the format from the JSON Response.
    • If you want real-time monitoring based on deviceScore , isDuplicate, or any other DQC data field , check the documentation for handling each case. We accept both valid requestIds and the special messages listed below when calling this function or sending transaction data to the dashboard.

Note: The requestId could be a valid requestId (20-character string) or one of these special messages:

Tools Demo

Tools Demo

Note: this is a step by step demo with UI components. Your integration needs to run all these steps in the background to get your requestId or DQC data depending on your integration needs

Survey ID:

Request ID:

Prerequisites

Before starting, ensure you have:

  • A React project set up (TypeScript or JavaScript).

Project Structure

Here's how your files should be organized after following this guide:

src/
├── components/
│ └── DQCIntegration.tsx # The integration component
├── utils/
│ ├── dqcToolbox.ts # DQC utility functions
│ └── dqcTermination.ts # (Optional) termination rules
└── pages/
└── SurveyPage.tsx # Your survey page using the integration

✅ Quick Start Checklist

Follow these steps in order:

  1. Get your API key from DQCO-OP platform
  2. Create dqcToolbox.ts in your src/utils/ folder
  3. Replace 'YOUR_DQC_API_KEY' with your actual API key
  4. Create DQCIntegration.tsx in your src/components/ folder
  5. Update the import path in DQCIntegration.tsx to match your file structure
  6. Add <DQCIntegration surveyId="your-survey-id" /> to your survey page
  7. Replace "your-survey-id" with your actual survey identifier (it can be a static string or dynamic value)
  8. Test in browser and check console for "DQC RequestId:" log
  9. Be careful with multiple calls as it will generate multiple requestIds. This can affect the duplicate check.

📌 Steps to Integrate DQC Toolbox with React

- Add Preconnect Hints

Add these <link> tags to your HTML <head> (e.g., index.html, or your framework's head management like Next.js <Head>):

<link rel="preconnect" href="https://api.dqco-op.com" crossorigin="anonymous" />
<link
rel="preconnect"
href="https://fpmetrics.dqco-op.com"
crossorigin="anonymous"
/>

This lets the browser establish connections to the DQC servers early, reducing latency when the toolbox initializes.

- API Key

Make sure you have your API key from the DQCO-OP platform. If you don't have one, follow these steps to get your API key.

- Create the DQC Toolbox Functions

Create a new file dqcToolbox.ts in your project (e.g., in a utils or services folder):

Note: For production applications, you can store your API configuration in environment variables. However, if this complicates your flow (especially in frameworks like Next.js), you can include the API key directly in your React app as shown above.

dqcToolbox.ts
// Replace with your actual API configuration
const API_BASE_URL = 'https://api.dqco-op.com';
const DQC_API_KEY = 'YOUR_DQC_API_KEY';

// Concise integration - only essential fields
export interface QualityToolsPayload {
requestId: string;
participantId: string;
dataTrustScore: number;
persona: string;
deviceScore: number;
isDuplicate: boolean;
country: string;
subdivision: string;
surveyId: string;
deviceFailures: string[];
}

interface DQCToolBoxModule {
getIdentityRequestId: (surveyId: string) => Promise<{ requestId: string }>;
}
let toolboxPromise: Promise<DQCToolBoxModule> | null = null;

// Default payload returned when a request can't be processed. Lets callers
// always rely on a valid object shape and never have to null-check.
const COULD_NOT_PROCESS_PAYLOAD: QualityToolsPayload = {
requestId: 'Could not process',
participantId: 'Could not process',
dataTrustScore: 0,
persona: 'NONE',
deviceScore: 0,
isDuplicate: false,
country: 'Could not process',
subdivision: 'Could not process',
surveyId: 'Could not process',
deviceFailures: ['Could not process'],
};

// Get requestId for a surveyId. Always returns a string — on failure returns
// the sentinel "Could not process" so callers don't have to null-check.
export const getDQCRequestId = async (surveyId: string): Promise<string> => {
if (typeof window === 'undefined') {
throw new Error('getDQCRequestId() must run on the client side');
}
if (!surveyId || surveyId.trim() === '') {
throw new Error('SurveyId is required to get RequestId');
}

const loadToolbox = async (): Promise<DQCToolBoxModule> => {
if (!DQC_API_KEY) throw new Error('DQC API key not provided');
if (!toolboxPromise) {
const DQC_TOOLBOX_URL = `${API_BASE_URL}/tools/toolbox/${DQC_API_KEY}`;
toolboxPromise = import(/* webpackIgnore: true */ DQC_TOOLBOX_URL).then(
(mod: Record<string, unknown>) => {
if (!('DQCToolBox' in mod)) {
throw new Error('DQCToolBox not found in module');
}
return mod.DQCToolBox as DQCToolBoxModule;
}
);
}
return toolboxPromise;
};

try {
const toolbox = await loadToolbox();
const { requestId } = await toolbox.getIdentityRequestId(surveyId);
return requestId;
} catch (error) {
console.error('Error getting DQC request ID:', error);
return 'Could not process'; // sentinel value so callers don't need to null-check
}
};

// Get DQC data for a requestId. Always returns a payload — on failure (or when
// the requestId is empty/invalid) returns the default "Could not process"
// payload so callers always get a valid object shape.
export const getDQCData = async (
requestId: string
): Promise<QualityToolsPayload> => {
if (!requestId || requestId.trim() === '') {
return COULD_NOT_PROCESS_PAYLOAD;
}
try {
const response = await fetch(`${API_BASE_URL}/tools/request/${requestId}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
Authorization: `apikey ${DQC_API_KEY}`,
},
});
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
const data = (await response.json()) as QualityToolsPayload;
return data;
} catch (error) {
console.error('Error fetching DQC data:', error);
return COULD_NOT_PROCESS_PAYLOAD;
}
};

- Create the DQC Integration Component

Create a reusable DQC integration component that handles all the tracking automatically:

DQCIntegration.tsx
import React, { useEffect } from 'react';
import { getDQCRequestId, getDQCData } from './dqcToolbox';

interface DQCIntegrationProps {
surveyId: string;
}

const DQCIntegration: React.FC<DQCIntegrationProps> = ({ surveyId }) => {
useEffect(() => {
const initializeDQC = async () => {
try {
const newRequestId = await getDQCRequestId(surveyId);
console.log('DQC RequestId:', newRequestId); // TODO: remove this log in production
// TODO: Store the requestId in your database or survey system.
// (You'll also want the participantId from getDQCData below — both are needed
// to populate the DQC dashboard via the Transaction API later.)

// Get DQC data — needed for participantId (always) and for real-time checks on
// isDuplicate / deviceScore / persona (optional).
const dqcData = await getDQCData(newRequestId);
console.log('DQC Data:', dqcData); // TODO: remove this log in production
// TODO: Store at minimum `requestId` + `participantId` alongside your survey results.
// Keep in mind the special requestId values (e.g. `Request-Blocked`, `Could not process`)
// — when one of these comes back, `dqcData` is the default "Could not process" payload
// (all-zero scores, `persona: 'NONE'`). See the "Terminating Low-Quality Respondents"
// section below for the `isScored` guard.
} catch (error) {
console.error('Failed to initialize DQC tracking:', error);
}
};

initializeDQC();
}, [surveyId]);

return null; // This component runs in background, no UI needed
};

export default DQCIntegration;

Important Notes:

  1. Store the requestId AND participantId: Save both to your database or survey system. The requestId is your link to the DQC dashboard; the participantId is what lets you send transaction data back to DQC so the dashboard is populated.
  2. Console logs: Remove all console.log statements in production

- Use in Your Survey Page

Now that you have created the DQCIntegration component, here's how to use it in your survey pages:

Key Principles:

  • Use a separate component for DQC integration that runs in the background
  • Avoid re-rendering issues by not placing DQC logic inside your main survey component
  • The integration only depends on the surveyId prop
SurveyPage.tsx
import React from 'react';
import DQCIntegration from './DQCIntegration';

function SurveyPage() {
const surveyId = 'YourSurveyID'; // use a static value or get it from props/state/url

return (
<div>
<h1>{surveyId}</h1>
<DQCIntegration surveyId={surveyId} />
<YourOriginalSurveyLogic />
</div>
);
}

// Your existing survey component
function YourOriginalSurveyLogic() {
return (
<div>
<h1>Your Survey Logic</h1>
</div>
);
}

export default SurveyPage;

This approach is simple because:

  1. Drop-in Integration: Just add <DQCIntegration surveyId={surveyId} /> to any survey page
  2. Zero Configuration: The component handles all DQC logic internally
  3. Background Processing: DQC data is collected and stored automatically
  4. No Props Drilling: Your existing survey components don't need to change their prop structure

Optional: Terminating Low-Quality Respondents

Termination is optional and should only be used if your research policy allows early termination. Start with store-only first: collect the DQC payload during your first rollout, validate the thresholds against your real respondent population, then turn terminations on incrementally.

These rules mirror the Decipher termination-script standard so a single research policy can drive both platforms consistently.

Key Points

  • Optional: use only if your research policy permits early termination.
  • Run AFTER getDQCData: terminations consume the resolved payload, never the raw requestId.
  • isScored guard for the score-based rules: passes through only Could not process and Submission too quick, data not processed (no data → respondent continues). Request-Blocked and Wrapper Tampering are evaluated normally — they typically return a low deviceScore and terminate via the score rule, which is what you want.
  • isDuplicate and persona rules are unguarded — they mirror the Decipher script. When the payload is the "no data" default (isDuplicate: false, persona: 'NONE'), neither rule fires, so no false termination.

A respondent is disqualified if any rule returns true:

RuleFieldDefault ThresholdUses isScored guard?
Very low device scoredeviceScore<= 10Yes (scored)
DuplicateisDuplicatetrueNo (unguarded)
Low data trust scoredataTrustScore< 250Yes (scored)
Keyboard Masherpersona'KEYBOARD MASHER'No (unguarded)

Lower scores = higher risk. These are the standard starter thresholds — adjust to your data after a calibration window.

A decision algorithm per rule

Each rule is its own small check. The two scored rules share a single isScored guard; the unguarded rules mirror the Decipher script and evaluate the payload directly. Pseudocode:

# Pass-through statuses — respondent continues, no scored rule fires.
PASS_THROUGH = ['Could not process', 'Submission too quick, data not processed']

# Guard for scored rules. Note: Wrapper Tampering and Request-Blocked are
# NOT in this list — they evaluate normally and typically terminate via
# the low-device-score rule.
function isScored(d):
return d.requestId not in PASS_THROUGH

# Rule 1 — Very low device score (scored)
function dqLowDeviceScore(d):
return isScored(d) and d.deviceScore <= 10

# Rule 2 — Duplicate (unguarded — mirrors the Decipher script)
function dqDuplicate(d):
return d.isDuplicate == true

# Rule 3 — Low data trust score (scored)
function dqLowDataTrust(d):
return isScored(d) and d.dataTrustScore < 250

# Rule 4 — Keyboard Masher (unguarded — mirrors the Decipher script)
function dqKeyboardMasher(d):
return d.persona == 'KEYBOARD MASHER'

Implementation

Create dqcTermination.ts next to your toolbox utility:

dqcTermination.ts
import type { QualityToolsPayload } from './dqcToolbox';

// Pass-through statuses — respondent continues, no scored rule fires.
// Note: `Request-Blocked` and `Wrapper Tampering` are NOT here — they
// evaluate normally and typically terminate via the low-device-score rule.
const PASS_THROUGH: readonly string[] = [
'Could not process',
'Submission too quick, data not processed',
];

// Guard for scored rules.
export const isScored = (d: QualityToolsPayload): boolean =>
!PASS_THROUGH.includes(d.requestId);

// Scored rules — guarded with `isScored`.
export const dqLowDeviceScore = (d: QualityToolsPayload): boolean =>
isScored(d) && d.deviceScore <= 10;

export const dqLowDataTrust = (d: QualityToolsPayload): boolean =>
isScored(d) && d.dataTrustScore < 250;

// Unguarded rules — mirror the Decipher termination script.
export const dqDuplicate = (d: QualityToolsPayload): boolean =>
d.isDuplicate === true;

export const dqKeyboardMasher = (d: QualityToolsPayload): boolean =>
d.persona === 'KEYBOARD MASHER';

// Combined — disqualify if ANY rule fires.
export const shouldTerminate = (d: QualityToolsPayload): boolean =>
dqLowDeviceScore(d) ||
dqDuplicate(d) ||
dqLowDataTrust(d) ||
dqKeyboardMasher(d);

Usage

Plug it into DQCIntegration.tsx after the getDQCData call:

import { shouldTerminate } from '../utils/dqcTermination';

// ...inside initializeDQC, after fetching dqcData:
const dqcData = await getDQCData(newRequestId);
if (shouldTerminate(dqcData)) {
// TODO: redirect to your termination page, or flag the respondent for your backend.
console.log('Terminate respondent:', dqcData);
}

Customization

  • Adjust thresholds to your data — e.g. deviceScore <= 25 for a more aggressive device-score rule, or dataTrustScore < 420 to align with the Decipher doc's aggressive end.
  • Disable a rule by removing it from shouldTerminate, or combine differently — replace || with && if you only want to terminate when multiple signals agree.
  • Add a new scored rule the same way — define a function that calls isScored(d) && /* your condition */.
  • Log instead of terminate during your first rollout so you can audit how many respondents would be flagged before you actually drop any.

Important Notes:

⚠️ Each component mount creates a new requestId: Every time the DQCIntegration component is mounted, it will generate a new requestId, even for the same surveyId. This allows tracking of multiple visits to the same survey.

⚠️ Component re-renders vs re-mounts: The component only creates a new requestId when it's mounted (not on re-renders). React's useEffect with [surveyId] dependency ensures it runs when the component mounts or when surveyId changes.

⚠️ For single-session tracking: If you want to prevent multiple requestIds for the same survey session, consider implementing session storage logic or move the DQCIntegration component to a higher level in your component tree. This may be needed in integrations like Next.js where the same surveyId is used across multiple pages/questions and needs to unmount/remount.

⚠️ Page refreshes and navigation: If the page is refreshed or the user navigates away and back, a new requestId will be generated. If this is expected in your app (e.g., a Next.js app or survey where you can come back later), to avoid this:

  • Consider using sessionStorage to persist the requestId within the browser session
  • Or implement a check in your backend to handle page refresh scenarios
  • Be aware that this may affect duplicate detection if not handled properly