Deprecation notice: NxVET is deprecating consultation record support in the near future. New integrations should use transcripts, conversations, webhooks, and other NxVET ecosystem data.

Receive Webhooks with Cloudflare Workers

Host your NxVET webhook endpoint on Cloudflare's free tier — no server to buy, no code to write yourself. Claude Code or Codex does the technical work; this guide tells you exactly what to ask for.

Last updated: July 31, 2026

Overview

NxVET webhooks need somewhere to go: a public HTTPS address that is always online. If you don't already run a server, Cloudflare Workers is the easiest way to get one. A Worker is a tiny program that Cloudflare runs for you in their data centers — it wakes up only when a webhook arrives, and the free plan (100,000 requests per day) is far more than any clinic will ever use.

Something happens in NxVET (recording finished, label created, ...)
        ↓
NxVET sends a signed webhook (HTTP POST)
        ↓
Your Cloudflare Worker  —  https://nxvet-webhook-receiver.YOUR-NAME.workers.dev
        ↓
The Worker checks the signature, then does whatever you want:
forward to email/Slack, call the NxVET API for the transcript, log it, ...

This guide is written for people who don't code. The pattern for every step is the same:

  1. You do the parts only you can do — creating accounts and approving logins in your browser.
  2. You copy a ready-made instruction (a "prompt") from this page into an AI coding assistant — Claude Code, Codex, or similar — and it runs the technical commands for you.
Total time: about 15 minutes, most of it waiting for account sign-ups. Everything in this guide uses free plans.

Prerequisites

  • An NxVET organization and access to the NxVET Integrations page
  • A computer with an AI coding assistant installed — Claude Code, Codex, Cursor, or similar. (If you don't have one yet, Claude Code's install page walks you through it.)
  • An email address for the free Cloudflare account

You do not need: a server, a credit card, or programming knowledge.

Step 1: Create a Free Cloudflare Account

This is the one part your AI assistant can't do for you — accounts and passwords should stay in your hands.

  1. Go to dash.cloudflare.com/sign-up.
  2. Sign up with your email and a strong password (a password manager is ideal). No credit card is needed.
  3. Verify your email address when the confirmation message arrives.
  4. If Cloudflare asks what you want to do, you can skip the onboarding questions — you don't need to add a website or domain. You only need the account to exist.
Good practice: Turn on two-factor authentication (2FA) in Cloudflare under My Profile → Authentication. This account will hold your webhook endpoint, so protect it like any other work account.

Step 2: Let Your AI Assistant Install the Tools

Cloudflare's command-line tool is called Wrangler. It needs Node.js (a free runtime) to work. Instead of installing these yourself, open your AI assistant in a terminal and paste this prompt:

Set up the Cloudflare Wrangler CLI on this machine:
1. Check if Node.js version 18 or newer is installed. If not, install the
   current LTS version using the standard method for this operating system.
2. Install Wrangler by running: npm install -g wrangler
3. Run: wrangler login
   This opens my browser so I can approve the login — tell me when to look
   at the browser, and wait for me to approve it.
4. When login completes, run: wrangler whoami
   and show me the result so I know it worked.
Explain each step in plain language as you go.

What happens: a browser tab opens asking you to allow Wrangler to access your Cloudflare account. Click Allow — this is you giving the tool on your own computer permission to deploy on your behalf. When wrangler whoami shows your email address, this step is done.

Only approve logins you started. The browser approval should appear seconds after your assistant runs wrangler login. If a Cloudflare permission page appears when you weren't expecting one, close it.

Step 3: Create the Worker

Now ask your assistant to build the webhook receiver. Paste this prompt:

Create a Cloudflare Worker project called "nxvet-webhook-receiver" in a new
folder. It receives webhooks from the NxVET veterinary platform.

Follow the official guide at https://api.nx.vet/cloudflare-webhooks.html —
use the exact Worker code from the "Worker code" section of that page.
Requirements, in case you cannot fetch the page:
- Only accept POST requests.
- Verify the X-NerveX-Signature header: it is "sha256=" followed by the
  HMAC-SHA256 hex digest of the raw request body, keyed with the secret in
  the NXVET_WEBHOOK_SECRET environment variable. Reject bad signatures
  with HTTP 401. Use a constant-time comparison.
- Respond 200 immediately and do any further work asynchronously.
- Handle event types: ping, new_label, label_updated, label_deleted,
  conversation_created, conversation_completed. For now, just log the
  event type and id (never log the full payload).
Do not deploy yet, and do not put any secret values in the code or config
files. Show me the project files when done.

Worker code

This is the code your assistant should produce (it can copy it from here verbatim). You don't need to read it — it's included so the page is self-contained and so a curious reader can see there's nothing mysterious going on:

// src/worker.js — NxVET webhook receiver
export default {
    async fetch(request, env, ctx) {
        if (request.method !== 'POST') {
            return new Response('Not found', { status: 404 });
        }

        // Read the raw bytes first — the signature covers the exact body
        const rawBody = await request.arrayBuffer();
        const signature = request.headers.get('X-NerveX-Signature') || '';

        const valid = await verifySignature(env.NXVET_WEBHOOK_SECRET, rawBody, signature);
        if (!valid) {
            return new Response('Invalid signature', { status: 401 });
        }

        const event = JSON.parse(new TextDecoder().decode(rawBody));

        // Acknowledge right away; do the real work in the background
        ctx.waitUntil(handleEvent(event, env));
        return new Response('OK', { status: 200 });
    }
};

async function verifySignature(secret, rawBody, signatureHeader) {
    if (!secret || !signatureHeader) return false;

    const key = await crypto.subtle.importKey(
        'raw', new TextEncoder().encode(secret),
        { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']
    );
    const mac = await crypto.subtle.sign('HMAC', key, rawBody);
    const expected = 'sha256=' + [...new Uint8Array(mac)]
        .map(b => b.toString(16).padStart(2, '0')).join('');

    const a = new TextEncoder().encode(signatureHeader);
    const b = new TextEncoder().encode(expected);
    if (a.byteLength !== b.byteLength) return false;
    return crypto.subtle.timingSafeEqual(a, b);
}

async function handleEvent(event, env) {
    // Never log full payloads — event type and id are enough for debugging
    console.log(`NxVET event: ${event.type} (${event.id})`);

    switch (event.type) {
        case 'ping':
            // Test event from the NxVET "Test" button — nothing to do
            break;
        case 'conversation_completed':
            // event.data.conversationId and event.data.deviceId are available.
            // This is where you add YOUR action: call the NxVET API for the
            // transcript, forward a notification, etc.
            break;
        case 'new_label':
        case 'label_updated':
        case 'label_deleted':
            // event.data.labelId is available.
            break;
    }
}

And the small configuration file next to it:

# wrangler.toml
name = "nxvet-webhook-receiver"
main = "src/worker.js"
compatibility_date = "2026-07-01"
Want it to actually do something? The code above safely receives and verifies events but takes no action yet — that's deliberate, so you can confirm the plumbing works first. Once everything is tested (Step 7), tell your assistant what you want, e.g. "When a conversation_completed event arrives, fetch the conversation from the NxVET API and email me a summary." The Examples page and AI agent reference give your assistant everything it needs.

Step 4: Deploy and Get Your URL

Paste this prompt:

Deploy the nxvet-webhook-receiver Worker with "wrangler deploy". If
Cloudflare asks to register a workers.dev subdomain, help me pick one.
When it finishes, show me the public https URL of the Worker.

The first deploy may ask you to choose a workers.dev subdomain — a name like bright-clinic that becomes part of your URL. Pick anything; it's just an address. The result is your webhook URL:

https://nxvet-webhook-receiver.YOUR-SUBDOMAIN.workers.dev

Copy it — you'll paste it into NxVET in the next step.

Step 5: Register the Webhook in NxVET

Now tell NxVET to send events to your new Worker:

  1. Sign in to app.nx.vet and open the Integrations page.
  2. Select the Webhooks tab.
  3. Click to add a new webhook and fill in:
    • Payload URL — your Worker URL from Step 4.
    • Description — e.g. Cloudflare Worker receiver.
    • Event types — tick only what you need (see the event types table). Ticking nothing sends all events.
  4. Click Create Webhook.
Copy the secret now. NxVET shows a secret starting with whsec_ only once, right after creation. Copy it into a password manager immediately — you need it in the next step. If you miss it, no harm done: delete the webhook and create it again.

Step 6: Store the Secret in Your Worker

The Worker uses the whsec_ secret to verify that each webhook genuinely came from NxVET. It must be stored as an encrypted Cloudflare secret — never in the code. Paste this prompt:

Run "wrangler secret put NXVET_WEBHOOK_SECRET" for the
nxvet-webhook-receiver Worker. It will prompt for the value — let me type
or paste the secret into the terminal prompt MYSELF. Do not ask me to give
you the secret, and do not echo or store it anywhere.

When the terminal asks for the value, paste your whsec_... secret and press Enter. Wrangler encrypts it and stores it with the Worker; from now on the code can use it but nobody can read it back — not even you.

Why the fuss? Pasting secrets into chat messages puts them in logs and history. Typing them into the terminal prompt keeps them out of both. The same rule applies to your nxvet_sk_ API key if you add one later.

Step 7: Test It

NxVET has a built-in test button, and Wrangler can show you the Worker's live logs. Paste this prompt:

Run "wrangler tail" for nxvet-webhook-receiver so we can watch its live
logs. Then tell me to click the Test button for my webhook on
https://app.nx.vet/integrations (Webhooks tab). Confirm from the logs
whether a "ping" event arrived and was accepted, and explain the result.

Click Test on your webhook row in the NxVET Integrations page. Within a second or two, the logs should show:

NxVET event: ping (evt_test_...)

You can also confirm from the NxVET side: expand your webhook row on the Integrations page to see the delivery history — the test delivery should show status 200.

That's it — your webhook endpoint is live. From here, tell your assistant what you actually want to happen when events arrive.

Bonus: Host a Companion Web App — Free, No Domain Needed

The same Cloudflare account can also host a full web app — say, a small dashboard that shows the events your Worker has received, or an internal tool for your team. Cloudflare Pages gives every project a free, permanent public URL like:

https://my-clinic-dashboard.pages.dev

You choose the first part of the name; HTTPS is automatic; there is nothing to buy. The free plan includes unlimited requests and bandwidth for static content and 500 deployments per month — more than enough for any clinic tool.

Because you already installed Wrangler and logged in (Step 2), publishing an app is one prompt away:

Build me a small web app: [describe what you want — for example, "a
single-page dashboard that explains our NxVET webhook setup to staff"].
Put it in a new folder, then publish it with Cloudflare Pages using
"wrangler pages deploy" under the project name "my-clinic-dashboard"
(help me pick a name if that one is taken — names are first-come,
first-served worldwide). When it's done, give me the public
https://....pages.dev URL.

Good to know:

  • Frontend vs. backend: Pages hosts the part users see; anything that needs secrets or talks to the NxVET API belongs in a Worker (like the one from this guide). The two work together on the same account. Never put an nxvet_sk_ API key in a web page — pages are public, and anyone can read their code.
  • Auto-deploy from GitHub: if your app lives in a GitHub repository, you can connect it in the Cloudflare dashboard (Workers & Pages → Create → Pages) and every push publishes automatically — no Wrangler needed after the first setup.
  • Preview URLs: each deploy also gets its own unique URL, so you can check a new version before it replaces the live one.
  • When you outgrow the free name: for a customer-facing app you'll eventually want a real domain. Cloudflare sells them at cost (roughly $10/year) and connecting one to a Pages project is a couple of clicks — hosting stays free.

Good Practices

  • Never put secrets in code or chat. The whsec_ secret and any nxvet_sk_ API key go in via wrangler secret put, typed by you at the terminal prompt. If a secret ever leaks, delete the webhook (or revoke the key) in NxVET and create a new one — takes a minute.
  • Always verify the signature. The code in this guide already does. Anyone on the internet can send POST requests to your Worker URL; the signature check is what makes forgeries bounce off with a 401.
  • Answer fast, work later. NxVET counts your webhook as failed if it responds slowly. The Worker responds 200 immediately and does follow-up work in the background (ctx.waitUntil) — keep it that way when you add features.
  • Expect duplicates and out-of-order events. NxVET delivers at least once. If your action must not happen twice (e.g. creating a task), ask your assistant to deduplicate by the event id.
  • Don't log patient data. Log event types and ids for debugging, not payloads or transcripts. Cloudflare logs are for plumbing, not medical records.
  • Check the delivery history when something seems off. The NxVET Integrations page shows the last 50 delivery attempts per webhook, with status codes — usually the fastest way to see which side has the problem.
  • Free tier is plenty. The Workers free plan allows 100,000 requests per day. A busy clinic generates a few hundred events per day at most. You'll never see a bill unless you opt into a paid plan.
  • Keep the account safe. 2FA on Cloudflare, and don't share the account. If several people need access, Cloudflare supports adding members with limited roles.

Troubleshooting

Symptom Likely cause & fix
Test ping shows 401 in delivery history The secret in the Worker doesn't match. Re-run Step 6 and paste the exact whsec_ value for this webhook (each webhook has its own). If you no longer have it, delete and recreate the webhook, then store the new secret.
Test ping shows 404 The Payload URL is wrong or the Worker isn't deployed. Check the URL in NxVET matches Step 4's URL exactly, and ask your assistant to run wrangler deploy again.
Nothing appears in wrangler tail The request never reached the Worker — usually a typo in the Payload URL. Delivery history in NxVET will show a connection error in that case.
wrangler login never opens a browser Copy the URL Wrangler prints in the terminal and open it in your browser manually.
Webhook was working, then stopped After repeated failures NxVET disables a webhook automatically. Fix the cause, then re-enable it from the Integrations page (or the enable endpoint).

Stuck? Ask your assistant to read the delivery history and the wrangler tail output side by side — between the two, the failure point is almost always obvious. And support@nx.vet is happy to help.

Checklist