PureText Buyer API Integration in Decipher
OVERVIEW
This API checks for the Open-Ended (OE) survey responses to ensure data quality. It performs language comparison, AI-generated answer detection, exclusion list checks, and PureText deduplication. Below is the processing flow:
-
Language Compare: Compares the language of the OE answer with the survey
language determined from the transaction ID - AI Detection: Detects whether the answer is AI-generated
-
Exclusion List: Checks if the answer is in an exclusion list of undesirable
responses -
PureText Deduplication: Ensures the answer's uniqueness through
deduplication
Pre-Requisites
- Need to add below host names in the decipher directory.
- For Production: https://spectrumsurveys.com/
- For Staging: https://staging.spectrumsurveys.com/
- API Access token
- Valid transaction_id
-
question_id: string or number or alpha numeric.
- Examples are Q1, S1 etc.
- text: Survey Response to validate.
-
Endpoint
- POST /buyers/v3/transactions/ps_api_fail
How to get API access token from Market Place
If you are an API user, you already have a API access token or one could be created for
you. Please contact your Account manager to know how to get the access token for your
account.
How to Program in Decipher
-
Step 1: Create a res tag with the access-token as we need to use this at multiple
questions. Please see the orange highlighted in step 3 to know how we use res
tag in api call.
<res label="access_token">xxxxxxxx-xxxxxxxxxplace access token
herexxxxxxxxxxxxxxxx </res>
-
Step 2: Call the API block at the end of the survey and update the question ids
(as highlighted in pink).- If the question is a textarea question, please update the row text with the
question id. - If it is an other specify option, please update the row text as
question_id#row#open. For example, there is a question Q2 and code r4 is
another specify option, then we need to use the syntax as Q2#r4#open. - If the question type is a text question with multiple rows, then use the syntax
as question_id#row. For example, there is a question Q3 and codes r1 and
r2, then we need to use the syntax as Q3#r1 and Q3#r2. - If the question type is text question with multiple rows and multiple columns,
then use the syntax as question_id#row#column. For example, there is a
question Q4 row r1 and column c1, then we need to use the syntax as
Q4#r1#c1
- If the question is a textarea question, please update the row text with the
API Block
<suspend/>
<block label="PureText_Block_Set1" cond="list=='127'" sst="0">
<note>----------------------API start----------------</note>
<exec when="init">
def func(value):
return ' '.join(value.splitlines())
</exec>
<suspend/>
<text
label="ndpRawSet1"
where="execute,survey,report">
<title>Base question for Open ends punching</title>
<exec>
for eachrow in ndpRawSet1.rows:
str1 = eachrow.text.split("#")
if len(str1[0]) gt 0:
if len(str1)==1:
if allQuestions[str1[0]].any and len(allQuestions[str1[0]].val) gt 0:
eachrow.c2.val = func(allQuestions[str1[0]].unsafe_val)
eachrow.c1.val = str1[0]
if len(str1)==2:
if allQuestions[str1[0]].attr(str(str1[1])).any and len(allQuestions[str1[0]].attr(str(str1[1])).val) gt 0:
eachrow.c2.val = func(allQuestions[str1[0]].attr(str(str1[1])).unsafe_val)
eachrow.c1.val = str1[0]+"_"+str1[1]
if len(str1)==3:
if str1[2]=="open":
if allQuestions[str1[0]].attr(str(str1[1])).attr(str(str1[2])) and allQuestions[str1[0]].attr(str(str1[1]))
and len(allQuestions[str1[0]].attr(str(str1[1])).attr(str(str1[2]))) gt 0:
eachrow.c2.val = func(allQuestions[str1[0]].attr(str(str1[1])).unsafe_open)
eachrow.c1.val = str1[0]+"_"+str1[1]+"_open"
else:
if allQuestions[str1[0]].attr(str(str1[1])).attr(str(str1[2])).any and
len(allQuestions[str1[0]].attr(str(str1[1])).attr(str(str1[2])).val) gt 0:
eachrow.c2.val = func(allQuestions[str1[0]].attr(str(str1[1])).attr(str(str1[2])).unsafe_val)
eachrow.c1.val = str1[0]+"_"+str1[1]+"_"+str1[2]
</exec>
<row label="r1">Q1</row>
<row label="r2">Q2#r4#open</row>
<row label="r3">Q3#r1</row>
<row label="r4">Q3#r2</row>
<row label="r5">Q3#r3</row>
<row label="r6"/>
<row label="r7"/>
<row label="r8"/>
<row label="r9"/>
<row label="r10"/>
<row label="r11"/>
<row label="r12"/>
<row label="r13"/>
<row label="r14"/>
<row label="r15"/>
<row label="r16"/>
<row label="r17"/>
<row label="r18"/>
<row label="r19"/>
<row label="r20"/>
<col label="c1">qid</col>
<col label="c2">text</col>
</text>
<suspend/>
<suspend/>
<text
label="PTstatusapiSet1"
randomize="0"
where="execute,survey,report">
<title>Status from API call</title>
</text>
<suspend/>
<exec>
#body
question_id1 = ndpRawSet1.r1.c1.val
question_id2 = ndpRawSet1.r2.c1.val
question_id3 = ndpRawSet1.r3.c1.val
question_id4 = ndpRawSet1.r4.c1.val
question_id5 = ndpRawSet1.r5.c1.val
question_id6 = ndpRawSet1.r6.c1.val
question_id7 = ndpRawSet1.r7.c1.val
question_id8 = ndpRawSet1.r8.c1.val
question_id9 = ndpRawSet1.r9.c1.val
question_id10 = ndpRawSet1.r10.c1.val
question_id11 = ndpRawSet1.r11.c1.val
question_id12 = ndpRawSet1.r12.c1.val
question_id13 = ndpRawSet1.r13.c1.val
question_id14 = ndpRawSet1.r14.c1.val
question_id15 = ndpRawSet1.r15.c1.val
question_id16 = ndpRawSet1.r16.c1.val
question_id17 = ndpRawSet1.r17.c1.val
question_id18 = ndpRawSet1.r18.c1.val
question_id19 = ndpRawSet1.r19.c1.val
question_id20 = ndpRawSet1.r20.c1.val
text1 = ndpRawSet1.r1.c2.val
text2 = ndpRawSet1.r2.c2.val
text3 = ndpRawSet1.r3.c2.val
text4 = ndpRawSet1.r4.c2.val
text5 = ndpRawSet1.r5.c2.val
text6 = ndpRawSet1.r6.c2.val
text7 = ndpRawSet1.r7.c2.val
text8 = ndpRawSet1.r8.c2.val
text9 = ndpRawSet1.r9.c2.val
text10 = ndpRawSet1.r10.c2.val
text11 = ndpRawSet1.r11.c2.val
text12 = ndpRawSet1.r12.c2.val
text13 = ndpRawSet1.r13.c2.val
text14 = ndpRawSet1.r14.c2.val
text15 = ndpRawSet1.r15.c2.val
text16 = ndpRawSet1.r16.c2.val
text17 = ndpRawSet1.r17.c2.val
text18 = ndpRawSet1.r18.c2.val
text19 = ndpRawSet1.r19.c2.val
text20 = ndpRawSet1.r20.c2.val
transaction_id = transaction_id
datadict = '''{
"transaction_id": "%s",
"questions": [{"question_id": "%s","text": "%s"},{"question_id": "%s","text": "%s"},{"question_id":
"%s","text": "%s"},{"question_id": "%s","text": "%s"},{"question_id": "%s","text": "%s"},{"question_id":
"%s","text": "%s"},{"question_id": "%s","text": "%s"},{"question_id": "%s","text": "%s"},{"question_id":
"%s","text": "%s"},{"question_id": "%s","text": "%s"},{"question_id": "%s","text": "%s"},{"question_id":
"%s","text": "%s"},{"question_id": "%s","text": "%s"},{"question_id": "%s","text": "%s"},{"question_id":
"%s","text": "%s"},{"question_id": "%s","text": "%s"},{"question_id": "%s","text": "%s"},{"question_id":
"%s","text": "%s"},{"question_id": "%s","text": "%s"},{"question_id": "%s","text": "%s"}]
}''' %(transaction_id, question_id1,text1, question_id2,text2, question_id3,text3, question_id4,text4,
question_id5,text5, question_id6,text6, question_id7,text7, question_id8,text8, question_id9,text9,
question_id10,text10, question_id11,text11, question_id12,text12, question_id13,text13,
question_id14,text14, question_id15,text15, question_id16,text16, question_id17,text17,
question_id18,text18, question_id19,text19, question_id20,text20)
headerdict={'Content-Type': 'application/json; charset=utf-8','access-token': res.access_token }
</exec>
<logic label="lg_apiSet1" api:data="datadict" api:headers="headerdict" api:method="POST"
api:url="https://spectrumsurveys.com/buyers/v3/transactions/ps_api_fail" uses="api.1">
<title>API Integration</title></logic>
<exec cond="PTstatusapiSet1.val in ['',None]">
if lg_apiSet1.status == 200:
PTstatusapiSet1.val = str(lg_apiSet1.status)
PTapiSet1.val = lg_apiSet1.r;
else:
PTstatusapiSet1.val = "failure of api"
</exec>
<suspend/>
<textarea
label="PTapiSet1"
where="execute,survey,report">
<title>Hidden Var</title>
</textarea>
<suspend/>
<text
label="PTR_Set1"
altlabel="PT_"
size="40" where="execute,survey,report">
<title>Hidden in LIVE.</title>
<exec>
if lg_apiSet1.status == 200:
data = lg_apiSet1.r['data']
paf = agr = ir = dr = lm = 0
a_paf = []
a_agr = []
a_ir = []
a_dr = []
a_lm = []
a_oti = []
for i in range(len(data)):
qid = data[i]['question_id']
oti = data[i]['original_transaction_id']
if oti != None:
qidoti = qid+":"+oti
a_oti.append(qidoti)
if data[i]['puretext_api_fail'] == 1:
paf = paf+1
a_paf.append(qid)
if data[i]['ai_generated_response'] == 1:
agr = agr+1
a_agr.append(qid)
if data[i]['incoherent_response'] == 1:
ir = ir+1
a_ir.append(qid)
if data[i]['duplicate_response'] == 1:
dr = dr+1
a_dr.append(qid)
if data[i]['language_mismatch'] == 1:
lm = lm+1
a_lm.append(qid)
if paf ge 1:
PTR_Set1.puretext_api_fail_Result_Set1.val = paf
a_paf_r = []
[a_paf_r.append(x) for x in a_paf if x not in a_paf_r]
PTR_Set1.puretext_api_fail_QIDs_Set1.val = ', '.join(a_paf_r)
if agr ge 1:
PTR_Set1.ai_generated_response_COUNT_Set1.val = agr
a_agr_r = []
[a_agr_r.append(x) for x in a_agr if x not in a_agr_r]
PTR_Set1.ai_generated_response_QIDs_Set1.val = ', '.join(a_agr_r)
if ir ge 1:
PTR_Set1.incoherent_response_COUNT_Set1.val = ir
a_ir_r = []
[a_ir_r.append(x) for x in a_ir if x not in a_ir_r]
PTR_Set1.incoherent_response_QIDs_Set1.val = ', '.join(a_ir_r)
if dr ge 1:
PTR_Set1.duplicate_response_COUNT_Set1.val = dr
a_dr_r = []
[a_dr_r.append(x) for x in a_dr if x not in a_dr_r]
PTR_Set1.duplicate_response_QIDs_Set1.val = ', '.join(a_dr_r)
if lm ge 1:
PTR_Set1.language_mismatch_COUNT_Set1.val = lm
a_lm_r = []
[a_lm_r.append(x) for x in a_lm if x not in a_lm_r]
PTR_Set1.language_mismatch_QIDs_Set1.val = ', '.join(a_lm_r)
if len(a_oti) ge 1:
a_oti_r = []
[a_oti_r.append(x) for x in a_oti if x not in a_oti_r]
PTR_Set1.original_transaction_dedupe_id.val = ', '.join(a_oti_r)
</exec>
<row label="puretext_api_fail_Result_Set1">puretext_api_fail_Result_Set1</row>
<row label="puretext_api_fail_QIDs_Set1">puretext_api_fail_QIDs_Set1</row>
<row label="ai_generated_response_COUNT_Set1">ai_generated_response_COUNT_Set1</row>
<row label="ai_generated_response_QIDs_Set1">ai_generated_response_QIDs_Set1</row>
<row label="incoherent_response_COUNT_Set1">incoherent_response_COUNT_Set1</row>
<row label="incoherent_response_QIDs_Set1">incoherent_response_QIDs_Set1</row>
<row label="duplicate_response_COUNT_Set1">duplicate_response_COUNT_Set1</row>
<row label="duplicate_response_QIDs_Set1">duplicate_response_QIDs_Set1</row>
<row label="language_mismatch_COUNT_Set1">language_mismatch_COUNT_Set1</row>
<row label="language_mismatch_QIDs_Set1">language_mismatch_QIDs_Set1</row>
<row label="original_transaction_dedupe_id">original_transaction_dedupe_id</row>
</text>
<suspend/>
<radio
label="PT_apiSet1"
optional="1"
where="execute,survey,report">
<title>Hidden in LIVE</title>
<exec>
PT_apiSet1.val = PT_apiSet1.r2.index
if PTR_Set1.puretext_api_fail_Result_Set1.any and len(PTR_Set1.puretext_api_fail_Result_Set1.val) ge 1:
PT_apiSet1.val = PT_apiSet1.r1.index
</exec>
<row label="r1">Fail</row>
<row label="r2">Pass</row>
</radio>
<suspend/>
<term label="PureTextSet1Term" cond="PT_apiSet1.r1">Term at PT Set1</term>
<suspend/>
<note>----------------------API End----------------</note>
</block>
<suspend/>
Result
Result: Results are stored in 11 flags, and shows where the respondent has failed.
1. PT_puretext_api_fail_Result_Set1: Indicates the count of all the flags where the respondents failed. For example, if the respondent was flagged due to incoherent response at Q1 and language mismatch at Q2. Then this count will be 2 (consolidated count).
2. PT_puretext_api_fail_QIDs_Set1: Indicates the question ids (with comma separation) which the respondents failed. For example, if the respondent was flagged due to incoherent response at Q1 and language mismatch at Q2. Then it shows “ Q1, Q2”
3. PT_ai_generated_response_COUNT_Set1: Indicates the count of all questions where the respondent failed due to AI generated text.
4. PT_ai_generated_response_QIDs_Set1: Shows the list of all question ids with the comma separation where the respondent failed due to AI generated text.
5. PT_incoherent_response_COUNT_Set1: Indicates the count of all questions where the questions failed due to incoherent/junk response.
6. PT_incoherent_response_QIDs_Set1: Shows the list of all question ids with the comma separation where the questions are failed due to incoherent/junk response.
7. PT_duplicate_response_COUNT_Set1: Indicates the count of all questions where the questions failed due to duplicate response.
8. PT_duplicate_response_QIDs_Set1: Shows the list of all question ids with the comma separation where the questions failed due to duplicate response.
9. PT_language_mismatch_COUNT_Set1: Indicates the count of all questions where the questions are failed due to language mismatch.
10. PT_language_mismatch_QIDs_Set1: Shows the list of all question ids with the comma separation where the questions are failed due to language mismatch.
11. PT_original_transaction_dedupe_id: Only in case of PT Deduplicate fail, this shows the list of all question ids and the original transaction_id that was duplicated from.
Finally, the consolidated flag (PT_apiSet1) indicates fail/pass options for the entire transaction. It shows fail if the any flag fails from the above 11 flags. And pass if all the flags are passed. The pass/fail criteria can be programmed based on client’s acceptability criteria.
Termination Process
The response returned Fail by PureText API can be considered for termination by adding the termination tag at the end of the script (Please see the green highlighted lines in step 3). The client may also choose to just collect the data as a flag for a post study decision on its handling.
-
Step 3: ( in case of termination) Update the termination redirect for this API failure as
shown below for PureSpectrum sample.
Updating Termination Redirect
<samplesource keyring="sys/ps" list="127" sign="out">
<title>Pure Spectrum</title>
<invalid>You are missing information in the URL. Please verify the URL with the original invite.</invalid>
<completed>It seems you have already entered this survey.</completed>
<var name="transaction_id" unique="1"/>
<var name="PSID"/>
<var name="supplier_id"/>
<exit cond="qualified"
url="https://spectrumsurveys.com/surveydone?st=21&transaction_id=${transaction_id}"/>
<exit cond="terminated and PT_apiSet1.r1"
url="https://spectrumsurveys.com/surveydone?st=84&transaction_id=${transaction_id}"/>
<exit cond="terminated and (NDPSpeederCheck.r1 or NDPSTRLNRCheck.r1 or NDPTRAPFLAG2.r1)"
url="https://spectrumsurveys.com/surveydone?st=20&transaction_id=${transaction_id}"/>
<exit cond="terminated"
url="https://spectrumsurveys.com/surveydone?st=18&transaction_id=${transaction_id}"/>
<exit cond="overquota"
url="https://spectrumsurveys.com/surveydone?st=17&transaction_id=${transaction_id}"/>
</samplesource>
</samplesources>
Result Screenshot for Reference
Additional Information
PureText can bulk process a maximum of 20 open ends. Upon exceeding this limit, please follow the steps outlined below.
1. Create another block with the same code just by replacing the “Set1” with the
“Set2” ... “SetN”.
2. Follow the same steps 2 – 4 again.
3. In step 4, please update the condition as below –
<exit cond="terminated and (PT_apiSet1.r1 or PT_apiSet2.r1) "
l="https://spectrumsurveys.com/surveydone?st=84&transaction_id=${transaction_id}"/>