Kauntech can push every business card you scan or edit to an external destination automatically. Open the app, go to Configs ▸ Webhook, and choose one of the two tabs below. Both fire on every save — pick whichever fits how you work.
This option sends each lead to a tiny Google Apps Script that you deploy on your own Google Sheet. Because the script runs inside your account, Kauntech never has to ask you to sign in with Google — you simply paste the script's Web-app URL into the app. Each event gets its own tab, only the columns you pick are written, and edits update the same row.
When you deploy the Web app, set Execute as: Me and Who has access: Anyone. If access is "Only myself", Google returns a sign-in page instead of running your script, and Kauntech will show "delivery failing". Re-deploy and paste the new /exec URL to fix it.
The sheet you want your leads to land in.
Delete any sample code, paste the script below, and Save.
Execute as "Me", Who has access "Anyone". Authorize when prompted.
It ends in /exec. Paste it into the Google Sheets tab in Kauntech and Save.
Use the "Send test row" button in the app to confirm everything works.
You can also copy this straight from the app (the Copy Apps Script button). It is a generic row-writer — Kauntech sends the tab name, your chosen columns, and the values, so you never have to edit the script when you change your columns.
/**
* KaunTech → Google Sheets
* 1. Open the Google Sheet you want to fill.
* 2. Extensions ▸ Apps Script, delete any code, paste this, Save.
* 3. Deploy ▸ New deployment ▸ Web app.
* - Execute as: Me
* - Who has access: Anyone
* 4. Authorize, then copy the Web app URL (ends with /exec) into KaunTech.
*/
function doPost(e) {
var lock = LockService.getScriptLock();
try { lock.waitLock(20000); } catch (ignore) {}
try {
var body = JSON.parse(e.postData.contents);
var ss = SpreadsheetApp.getActiveSpreadsheet();
var tabName = String(body.tab || 'All Leads').slice(0, 99);
var sheet = ss.getSheetByName(tabName) || ss.insertSheet(tabName);
var headers = body.headers || [];
var values = body.values || [];
var idCol = headers.length; // hidden "_id" is always the last column
// First write to a fresh tab: lay down the header row.
if (sheet.getLastRow() === 0 && headers.length) {
sheet.getRange(1, 1, 1, headers.length).setValues([headers]);
sheet.setFrozenRows(1);
if (idCol > 0) sheet.hideColumns(idCol);
}
var id = String(body.id || '');
var event = String(body.event || 'contact.created');
// Find an existing row by hidden _id (update + idempotent retries).
var rowIndex = -1;
var lastRow = sheet.getLastRow();
if (id && idCol > 0 && lastRow > 1) {
var ids = sheet.getRange(2, idCol, lastRow - 1, 1).getValues();
for (var i = 0; i < ids.length; i++) {
if (String(ids[i][0]) === id) { rowIndex = i + 2; break; }
}
}
if (event === 'contact.deleted') {
if (rowIndex > 0) sheet.deleteRow(rowIndex);
return json({ ok: true, action: 'delete', tab: tabName, url: ss.getUrl() });
}
var targetRow = rowIndex > 0 ? rowIndex : sheet.getLastRow() + 1;
var range = sheet.getRange(targetRow, 1, 1, values.length);
range.setNumberFormat('@'); // plain text so +91… and =… aren't read as formulas
range.setValues([values]);
return json({ ok: true, action: rowIndex > 0 ? 'update' : 'create', tab: tabName, url: ss.getUrl() });
} catch (err) {
return json({ ok: false, error: String(err) });
} finally {
try { lock.releaseLock(); } catch (ignore) {}
}
}
function doGet() {
return json({ ok: true, service: 'KaunTech', ready: true });
}
function json(obj) {
return ContentService
.createTextOutput(JSON.stringify(obj))
.setMimeType(ContentService.MimeType.JSON);
}_id column lets edits update the same row instead of duplicating it.+ or = are stored as plain text, never parsed as formulas.Prefer to send leads to a CRM, Zapier/Make, or your own API? The Webhook tab fires an HTTPS POST with the lead's full JSON to any endpoint you control, every time you save a contact.
POST {your-url}
Content-Type: application/json
X-KaunTech-Event: contact.created
X-KaunTech-Signature: sha256=<hex>
X-KaunTech-Retry: 0
{
"id": "lead_uuid",
"event": "contact.created",
"name": "Jane Smith",
"title": "Head of Sales",
"company": "Acme Corp",
"email": "jane@acme.com",
"phone": "+1234567890",
"website": "acme.com",
"address": "...",
"conference": "TechWeek '26",
"lead_temperature": "hot",
"lead_score": 92,
"product_of_interest": ["enterprise"],
"notes": "Met at booth 14",
"scanned_at": "2026-05-24T18:00:00Z"
}Every request includes an X-KaunTech-Signature header so your server can confirm it really came from Kauntech. Recompute the HMAC over the raw request body using your signing secret (shown in the Webhook ▸ Settings tab) and compare:
hmac = HMAC_SHA256(secret, raw_body) expected = "sha256=" + hex(hmac) if (expected !== header) reject()
Respond with a 2xx status to acknowledge receipt. Kauntech retries on network errors and 5xx / 429 responses, and surfaces the last error in the app if delivery keeps failing. A 4xx (other than 429) is treated as a misconfiguration and dropped.
/exec URL.{ ok: true, ready: true }, the script is healthy.Still stuck? Email us at business@voltairtech.com and we'll help you get set up.