Follow-Up Studio
Inputs
1 · Prospect & Call Context

Who you spoke to and how the message must sound. The city and the time of day the e-mail/message(s) go out together drive the sign-off — “Best regards to Amsterdam + have a nice evening”, in German “Beste Grüße nach Amsterdam und einen schönen Abend”.

HubSpot connection

Create a private app in HubSpot (Settings → Integrations → Private Apps) with the scopes crm.objects.contacts.read and crm.objects.companies.read — plus crm.objects.contacts.write if you want Log in HubSpot to write the internal note back, since HubSpot files notes under the contacts scope. Paste its token here. It is stored in this browser only.

HubSpot's API sends no CORS headers, so a browser refuses every direct call to it. A tiny proxy has to sit in between. This project ships one, and it deploys with the app — nothing to maintain separately.
How to deploy the proxy — once, about three minutes

A · The app already runs on Cloudflare Pages from Git — the easy case.

  1. The file functions/hubspot/[[path]].js is already in the repository. Pages picks up anything under functions/ at the repo root automatically — there is no code editor to find in the dashboard, and no separate Worker to create.
  2. Commit and push. The next deployment publishes the app and the proxy together.
  3. Leave this field empty. The app then calls its own domain, /hubspot/…, which is same-origin — CORS never even applies.
  4. Check it worked: open https://<your-domain>/hubspot/crm/v3/objects/contacts. A JSON error about a missing Authorization header is the good answer — it proves the proxy is live. A 404 means the deployment has not picked it up yet.

B · Anywhere else — opened from a file, served from localhost, or hosted somewhere without Functions.

  1. Cloudflare dashboard → Workers & PagesCreateWorkersCreate Worker. Name it hubspot-proxy, then Deploy.
  2. On the Worker's page click Edit code, delete the template, paste the contents of deploy/hubspot-proxy-worker.js from this project, then Deploy again.
  3. Settings → Variables and Secrets → add ALLOWED_ORIGINS with the origins that may call it, comma-separated, e.g. https://followup-studio.cxlever.io,http://localhost:8777.
  4. Copy the Worker URL (https://hubspot-proxy.<subdomain>.workers.dev) into this field.
  5. One Worker can serve every CxLEVER app that talks to HubSpot. List each app's origin in ALLOWED_ORIGINS, separated by commas, and paste the same URL into each app. The Worker holds no token of its own, so nothing is shared between the apps except the route.

Already connected a Worker to Git and it will not take variables? Cloudflare then says “Variables cannot be added to a Worker that only has static assets” — wrangler deploy found no wrangler.toml, so it published the repository as files instead of running the script. Route A makes that Worker unnecessary; disconnect or delete it.

Either way the proxy stores nothing and holds no secret. Your private app token travels in the request header from this browser, exactly as it would to HubSpot directly, and only read paths are forwarded — a leaked URL cannot write into your CRM.

Gmail draft — optional, creates the e-mail as a draft in your own Gmail

The finished e-mail is pushed straight into your Gmail drafts, formatting and subject already in place — you open Gmail, read it once and hit send. Google requires one thing that cannot be shipped inside an app: a Client ID of your own. It takes about five minutes, once.

Stored in this browser only. The access token Google returns is kept in memory, never written to disk, and expires by itself.
Filled automatically when the contact came from HubSpot. Leave it empty and the draft is created without a recipient.
How to get the Client ID — once, about five minutes
  1. Open console.cloud.google.com ↗ and sign in with the Google account whose drafts you want to write into.
  2. Top left, next to the logo, open the project picker → New project. Name it Follow-Up StudioCreate, then make sure it is the selected project.
  3. APIs & Services → Library, search for Gmail API, open it and press Enable.
  4. APIs & Services → OAuth consent screen. Google Workspace account: choose Internal — then nothing else is needed. Private Gmail: choose External. Fill in the app name, your e-mail as support contact and as developer contact → Save and continue.
  5. On the Scopes step press Add or remove scopes, filter for gmail.compose, tick .../auth/gmail.composeUpdateSave and continue. That scope can create drafts and nothing else — it cannot read your mail.
  6. External only: on Test users press Add users and add your own address. While the app is in Testing, only listed users may use it — which for a private tool is exactly right. No Google review is required.
  7. APIs & Services → Credentials → Create credentials → OAuth client ID. Application type Web application, name Follow-Up Studio.
  8. Under Authorised JavaScript origins press Add URI and enter the address this app runs on, with no path and no trailing slash — for example https://followup-studio.cxlever.io. Leave Authorised redirect URIs empty. → Create.
  9. Copy the Client ID (it ends in .apps.googleusercontent.com) and paste it into the field above.
  10. Press Create the draft in Gmail. Google asks once for permission; after that it is silent. The draft appears in Gmail under Drafts.

This only works when the app is served over https — Google refuses a file:// origin. On this build the origin to register is .

Nothing passes through a CxLEVER server: the browser talks to Google directly, the token lives in memory for the length of the session, and Disconnect throws it away.

* Paste the actual website/webpage content or scrape(s) into 2 · Inputs below. The URL alone is only a hint for the AI — pasted content is what stops it from inventing things.
The HubSpot import fills both from the contact’s associated companies — the primary company first, the second company after it. With only one company, the second field takes that company’s other URL when its website and domain differ.
2 · Inputs — Transcript, Website, CRM

The message can only be as deep as what you paste here. Block A is mandatory; B and C are what make the mirror paragraph land — every fact the AI uses from them is listed back to you in the internal note.

Drag & drop one or more files straight onto this box — .txt, .md, .vtt and .srt subtitles, .json, .csv. Several files are appended one after another, each under its own filename header.
Drop saved pages or scrapes here — .html files are reduced to their readable text automatically. The scrape buttons read the site’s own sitemap, group the pages by section, and take only what you tick — through the same proxy the HubSpot import uses.

Nothing you paste leaves this browser unless you press Generate in the Live Generate panel — and then it goes straight to the AI provider you chose, never to a CxLEVER server.

3 · Preset & Next Step

The preset decides the architecture of the message — which blocks appear, in which order, and how long each piece may be.

Short stands on its own, for prospects who get no e-mail. The note right after the e-mail does one job: it says the detailed analysis has just landed in their inbox, names the topics it covers, and invites a reply.
The last two shape the e-mail only. Copy E-Mail keeps that formatting when you paste into Gmail, Outlook or HubSpot; the plain-text and LinkedIn versions drop it, because neither channel renders it.
Both are written into the message only if the inputs support them — the NDA sentence asks whose template to use, and the call attempt names the number you actually tried, taken from the CRM block.
The field above shapes the closing paragraph: who sits at the table and what you need before the meeting. This one is for content the AI must weave in wherever it fits — a commitment made on the call, a name to credit, a constraint to acknowledge.

For the deepest messages, paste this prompt into one of the latest top models — e.g. Claude Opus 4.8, Gemini 3.1 Pro, GPT-5.x or Mistral Large — then paste the JSON it returns into the Spec editor below.

Follow-Up Spec Editor

The message set as JSON — the file everything else is rendered from. You rarely need to open this: generate below, edit in the preview. It is here for writing a message by hand, for pasting JSON back from an AI, and for reading the error when something does not parse.

Live Generate (BYOM) & Refine

Runs the same prompt against your own AI provider. Everything is optional — the app works fully offline without it. Keys are stored only in this browser and sent directly to the provider you pick, over HTTPS. Nothing else leaves your machine.

A full message set (e-mail plus the LinkedIn versions and the internal note) needs roughly 4.000–8.000 output tokens. Too low truncates the JSON.
Quality Check

Scores the message set against the CxLEVER follow-up rules — salutation, capitalised Du-forms, forbidden phrases, double punctuation, bullet symbols, sign-off, length, one CTA — plus a hallucination check that flags every number and every product or vendor name in the message that appears nowhere in your inputs.

Tone of Voice / House Rules — Sender, Forbidden Phrases, Subjects

Your standing rules. They go into every prompt and every quality check, so the app enforces the same style each time.

Tone-of-Voice Profiles

Keep your own tone-of-voice prompts here and pick the one this message should follow. The AI receives the profile name, the link and — if you paste it — the text itself. Paste the text: no AI can open your Google Doc from inside this app.

Versions & Snapshots

Save snapshot stores a dated copy of the current message set with its quality score. Diff shows exactly what changed against the set you have open now — useful to compare the draft you generated with the one you actually sent. Snapshots live in this browser only.

Run Diagnostics

Self-test of the app's dependencies and current config. No data is sent unless you click “Test connection” above.

Message Preview
CxLEVER Follow-Up Studio v1.9.1 · spec schema v1.1 · offline core
Made with in Germany. Powered by CxLEVER.