DEMO
A few minutes after submitting, an order confirmation URL will be sent.
Workflow App example

Trigger Code
<form id="send_po_tsv_trigger">
<label for="qEmail"><strong>Email Address / メールアドレス</strong>:</label><br>
<input id="qEmail" type="email" name="q_Email"
placeholder="you@example.com" required size="32" maxlength="64"><br><br>
<label for="qTsv"><strong>Purchase Orders TSV / 発注TSV</strong>:</label><br>
q_product - q_quantity - q_price - (q_amount:calced)<br>
<textarea id="qTsv" name="q_line_items" rows="5" cols="33"></textarea>
<input id="qsubmit" type="submit" value="Submit / 送信">
</form>
<div id="send_po_tsv_log">A few minutes after submitting, an order confirmation URL will be sent.</div>
<script>
const strStartUrl = "https://example.questetra.net/System/Event/MessageStart/99/0/start"; // ★★
const strKey = "GdixCgUffsvDBqnsOPPQUVgWtmwEbsjC"; // ★★See your workflow app★★
const strIdForm = "#send_po_tsv_trigger"; // ★must be same as Trigger form id★
const strIdLog = "#send_po_tsv_log"; // ★must be same as Log div id★
const strIdTextarea = "#qTsv"; // ★
const strIdEmail = "#qEmail"; // ★
const elTriggerForm = document.querySelector ( strIdForm );
const elOutput = document.querySelector ( strIdLog );
const elTextarea = document.querySelector ( strIdTextarea );
const elEmail = document.querySelector ( strIdEmail );
elTextarea.addEventListener ( 'change', function(ev) { // Preview TSV in elOutput
//let elOutput = document.querySelector ( strIdLog );
let arr2dPo = parseAsRectangular( elTextarea.value );
let strTmpHtml = "";
strTmpHtml += "Entered TSV preview (" + arr2dPo[0].length + " cols " + arr2dPo.length + " rows)<br>";
strTmpHtml += "<table>";
for( let i = 0; i < arr2dPo.length; i++ ){
strTmpHtml += "<tr>";
for( let j = 0; j < arr2dPo[i].length; j++ ){
strTmpHtml += "<td style='padding:0px 2px'>" + encodeHTML( arr2dPo[i][j] ) + "</td>";
}
strTmpHtml += "</tr>";
}
strTmpHtml += "</table>";
elOutput.innerHTML = strTmpHtml;
}, false);
elTriggerForm.addEventListener ( 'submit', function(ev) {
ev.preventDefault();
//let elOutput = document.querySelector( strIdLog );
let arr2dPo = parseAsRectangular( elTextarea.value );
let strQtableXml = "";
strQtableXml += "<list>";
for( let i = 0; i < arr2dPo.length; i++ ){
strQtableXml += "<row>";
for( let j = 0; j < arr2dPo[i].length; j++ ){
strQtableXml += "<col>" + encodeHTML( arr2dPo[i][j] ) + "</col>";
}
strQtableXml += "</row>";
}
strQtableXml += "</list>";
let objFormData = new FormData();
objFormData.append ( 'q_Email' , elEmail.value ); // ★
objFormData.append ( 'q_line_items', strQtableXml ); // ★
objFormData.append ( 'key', strKey );
elOutput.innerHTML = "Uploading ...";
fetch( strStartUrl, { method: 'POST', mode: 'cors', body: objFormData } ).then( response => {
console.log( 'response: ' + response.ok + " " + response.status + " " + response.statusText );
// https://developer.mozilla.org/docs/Web/API/Response
if ( !response.ok ) { throw new Error( "response was not ok" ); } // go "catch"
return response.text(); // QBPMS responces the string PID.
})
.then( (text) => {
elOutput.innerHTML = "Accepted (ID: " + text + ").";
})
.catch( (error) => {
elOutput.innerHTML = error;
});
}, false);
////// functions
//// Escape XML/HTML
function encodeHTML ( str ){
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
.replace(/"/g, '"').replace(/'/g, ''');
}
//// Parses TSV string as two-dimensional rectangular data matrix and creates a 2D array.
function parseAsRectangular( strTsv ){
const arrTsv = strTsv.split("\n");
/// Get numMinWidth and numMaxWidth (blank lines are excluded)
let numMinWidth = Infinity; // cf. String-Type Max: 1 million
let numMaxWidth = 0;
let numBlanklines = 0;
for( let i = 0; i < arrTsv.length; i++ ){
if( arrTsv[i] === "" ){ // Skip blank lines
numBlanklines += 1;
continue;
}
let arrCells = arrTsv[i].split("\t");
if( numMinWidth > arrCells.length ){ numMinWidth = arrCells.length; }
if( numMaxWidth < arrCells.length ){ numMaxWidth = arrCells.length; }
}
console.log( " TsvDataCheck:" + " MinWidth:" + numMinWidth + " MaxWidth:" + numMaxWidth +
" Lines:" + arrTsv.length + " (BlankLines:" + numBlanklines + ")" );
/// Get numMinWidth and numMaxWidth (blank lines are excluded)
let arr2dTsv = [];
for( let i = 0; i < arrTsv.length; i++ ){
if( arrTsv[i] === "" ){ // Skip blank lines
continue;
}
let arrTmp = [];
let arrCells = arrTsv[i].split("\t");
for( let j = 0; j < numMaxWidth; j++ ){
if( j < arrCells.length ){
arrTmp[j] = arrCells[j];
}else{
arrTmp[j] = "";
}
}
arr2dTsv.push( arrTmp );
}
return arr2dTsv;
}
</script>