Public
Receives tawk.to chat transcripts, matches to Salesforce
salesforcetawkwebhook
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data – all from the browser, and deployed in milliseconds.

tawk.to chat transcripts → SQLite → Salesforce match

Receives tawk.to webhooks, stores the full message body of every chat, scrapes contact details out of what the visitor typed, and checks whether that person is already in Salesforce.

Free and ungated — this uses tawk webhooks, not the beta REST API, and needs no plan upgrade.

Endpoints

POST /webhook receiver. Put this URL in tawk Admin → Webhooks.
GET /enrich.jsagent-facing enrichment script for your website (public, no secrets)
GET /?key=…list of chats
GET /?key=…&chat=<id>one chat: full transcript, what was scraped, Salesforce match

Setup

  1. In tawk: Admin → Webhooks → add a webhook pointing at this val's URL. Subscribe to chat:transcript_created (the one carrying the message body); chat:start and ticket:create are also handled. Set a secret.
  2. Set the env vars below.
KeyRequiredWhat it does
TAWK_WEBHOOK_SECRETyesThe secret from step 1. Requests failing HMAC are rejected with 401.
VIEW_KEYto read anythingAny random string. Transcripts are customer PII, so the viewer stays shut until this is set. Use letters and digits only — it goes in a URL.
SALESFORCE_AGENT_URLfor matchingEndpoint of tribeca-salesforce-agent. Copy from tribeca-outbound-sales-call.
SALESFORCE_AGENT_KEYfor matchingThat val's API_SHARED_SECRET.

Without the Salesforce pair, transcripts still land — matches just report error.

enrich.js — showing agents who they're talking to

Add this to tribecabeverage.com above the tawk embed (tawk reads its Tawk_API callbacks at init, so load order matters):

<script src="https://tribeca-tawk-transcripts.val.run/enrich.js"></script> <!-- your existing tawk embed goes below this -->

It hooks onChatMessageVisitor and onPrechatSubmit, scans what the visitor types, and writes these into tawk's visitor attributes so they show in the agent's Details panel:

AttributeSource
visitor-phonescraped from the visitor's own messages
visitor-emailscraped
mentioned-zipscraped (phone digits stripped first so they can't be mistaken for a ZIP)
sf-statusexisting customer / existing customer - multiple accounts / no account match
sf-accountSalesforce account name, when exactly one matched

Values accumulate across the conversation, so a number given in message one is still on screen when an agent joins at message six.

Custom attributes need no Secure Mode — only name/email do, which is why these use dashed custom keys.

The /enrich-lookup tradeoff — read before changing it

sf-status and sf-account require a lookup the visitor's browser performs, so /enrich-lookup cannot carry a secret. Anything it returns is readable by anyone who finds the URL: feed it phone numbers, learn which are customers and what those accounts are called.

That was accepted deliberately. The controls that bound it:

  • phone only — Salesforce exposes no email field to this integration user
  • minimal responseknown, ambiguous, account_name. Never a delivery date, address, other contact, or record id. Do not widen this.
  • per-IP rate limit — 15/hour (verified: 16th request returns 429)
  • Origin allow-list — non-Tribeca origins get 403 (friction only; a header is trivially forged with curl)
  • every call logged with IP and outcome, so abuse is visible

The clean fix, if it ever matters: tawk's REST API (beta, gated behind their access-request form) would let this val set visitor attributes server-side, and /enrich-lookup could then be deleted entirely.

Two deliberate design decisions

Only visitor messages are scraped for contact details. Agents paste the office number and support address constantly; scraping the whole transcript would match every single chat to Tribeca itself. Verified: given a thread where Michael pastes 973-913-4568 and info@tribecabeverage.com, the scraper returns only the visitor's own dana@acmedeli.com / 2015550147.

Matching is by email and phone, never by name. Names aren't unique and a confident wrong match is worse than no match.

Do not reuse this for a customer-facing bot

The Salesforce match here is safe because the result is only ever shown to your own team. Wiring the same lookup into a bot that tells a visitor whether an account exists would let anyone probe your customer list — type a name, learn whether that business is a customer, then get handed their address. If that capability is wanted in Apollo, it needs to match on something the real customer would know before revealing anything.

  • tribeca-afterhours-chat — the standalone after-hours widget (superseded if tawk's Apollo AI agent gets turned on)
  • tribeca-salesforce-agent — the read-only SOQL proxy this queries