Public
Webhook receiving Vapi call transcripts for sentiment analysis
apivapiwebhook
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.

Vapi Transcript Webhook (Borrower Support)

Receives Vapi end-of-call webhooks for your borrower-support assistant, extracts the call transcript, and stores it — agent turns and borrower turns kept separate. You pull the stored transcripts into a Google Colab notebook and run sentiment analysis with the OpenAI API.

Endpoints

Try the app

POST / — Vapi webhook receiver

Point your Vapi assistant's Server webhook URL at this endpoint. It handles the end-of-call-report event (where Vapi wraps the call in message.call) and stores the split transcript.

{ "ok": true, "callId": "borrow-789", "transcript": { "agent": "I understand this is stressful...\nYes, we can discuss a deferment plan...", "borrower": "I'm really struggling financially...\nCan I defer for a few months?...", "full": "borrower: ...\nagent: ...\nborrower: ...\nagent: ..." } }

GET / — retrieve stored transcripts

Returns every stored call, newest first, as JSON — this is what the Colab notebook fetches.

{ "calls": [ { "id": "borrow-789", "received_at": "2026-09-03T22:01:56.000Z", "agent_transcript": "...", "borrower_transcript": "...", "full_transcript": "..." } ] }

Only these fields are stored — no call duration, phone number, or other metadata.

Setting the webhook URL in Vapi

  1. In the Vapi dashboard, open your borrower-support assistantServerWebhooks.
  2. Add the webhook URL: https://vapi-transcript-webhook.val.run/
  3. Subscribe to the End-of-call report event.

Sentiment analysis in Google Colab

Paste this into a Colab cell. Fill in your OpenAI key where noted. It fetches all transcripts and runs separate sentiment analysis on the agent's text and the borrower's text.

# ---------- 1. SETUP ---------- # Paste your OpenAI API key here (or use Google Colab secrets). OPENAI_API_KEY = "sk-..." # <-- put YOUR key here WEBHOOK_URL = "https://vapi-transcript-webhook.val.run/" import requests from openai import OpenAI client = OpenAI(api_key=OPENAI_API_KEY) # ---------- 2. FETCH ALL TRANSCRIPTS ---------- data = requests.get(WEBHOOK_URL).json() calls = data["calls"] print(f"Found {len(calls)} stored call(s).\n") # ---------- 3. SENTIMENT ANALYSIS ---------- def analyze(text: str) -> str: """Return a sentiment label + one-line summary for a piece of text.""" if not text.strip(): return "(no text)" resp = client.chat.completions.create( model="gpt-4o-mini", messages=[{ "role": "user", "content": ( "Classify the sentiment as positive, negative, or neutral and give a " "one-line summary. Respond in this exact format:\n" "Sentiment: <label>\nSummary: <one line>\n\n" f"Text:\n{text}" ), }], ) return resp.choices[0].message.content for call in calls: print("=" * 50) print("Call ID:", call["id"]) print("Received:", call["received_at"]) print("\n--- AGENT sentiment ---") print(analyze(call["agent_transcript"])) print("\n--- BORROWER sentiment ---") print(analyze(call["borrower_transcript"])) print() # ---------- 4. (Optional) TABULAR SUMMARY ---------- # Uncomment to build a quick table of borrower sentiment per call. # import pandas as pd # summary = [] # for call in calls: # resp = client.chat.completions.create( # model="gpt-4o-mini", # messages=[{ # "role": "user", # "content": ( # "Answer with exactly one word: positive, negative, or neutral.\n\n" # f"Borrower text:\n{call['borrower_transcript']}" # ), # }], # ) # label = resp.choices[0].message.content.strip().lower() # summary.append({"call_id": call["id"], "borrower_sentiment": label}) # print(pd.DataFrame(summary))

To store your key as a Colab secret instead of in the code:

from google.colab import userdata OPENAI_API_KEY = userdata.get("OPENAI_API_KEY")