AI Workflows
Run your own LLM tool — Claude Code, Codex, Cursor, a script — when something happens in NxVET
Last updated: October 8, 2026
- Tool surface for the model:
https://mcp.nx.vet/mcp(33 tools; MCP page)- Event source, in the clinic:
- NxVET Agent app —
nxvet-headless workflow-add --template claude-code --on voice_task_confirmed - Event source, in the cloud:
- Webhooks — subscribe
task_updated,new_label, … - The hand-off event:
- a voice-filed task leaves
needs_confirmation→task_updated(agent:voice_task_confirmed) - Machine-readable:
- llms-full.txt §2.16 (voice turns), Recipe 6
Overview
NxVET is the place where a clinic's conversations, recordings, tasks and whiteboard already live, and it tells you the moment any of them changes. An AI workflow is a small loop on top of that: an event happens → your LLM tool is started with the event and its record → the tool does the work through the NxVET MCP tools (or the REST API) → it writes the outcome back (a comment, a status, a new task) so staff and the next run see it.
You bring the tool. Claude Code, OpenAI Codex, Cursor, an in-house script — anything with a command line or a webhook receiver. NxVET brings the events, the data, the tools the model calls, and the place where people approve and see the result.
A. In the clinic — NxVET Agent app
The agent runs on a clinic PC, signed in, on the organization's event stream. workflow-add
points a command at events; the agent runs it with the event and record as JSON.
No public endpoint, no server. Best when the tool should run where the clinic's own files and systems are.
B. In the cloud — webhooks
Your endpoint receives signed events; your worker fetches the record and spawns the tool (or calls a model API) with MCP as its tool surface.
Best when you already run a service, or want one workflow for many clinics.
C. Interactive — MCP
Connect any MCP client and just ask: "what did the badges ask for today?", "confirm it", "file the follow-ups from this visit".
Best for people; the same tools the automated paths use.
The voice-assistant hand-off
The first workflow most clinics build. An NxHUB badge in assistant mode is a personal assistant: hold the
button, speak, hear the answer. When the person asks for something to be done —
"add a task to write me a prescription for gabapentin for Bella" — the assistant files a task on the
organization's Voice assistant board in NxVET as Needs confirmation and says so
(the turn, its transcript, reply and per-step timings are readable:
GET /api/nxhub/voice-turns, MCP list_voice_turns).
- Spoken. The task is a request: status
needs_confirmation,task_createdfires. Do not act yet. - Confirmed. A person moves it to any other non-dismissed status on the NxVET Tasks page (or an MCP client does with
update_task).task_updatedfires — the hand-off. - Done by your tool. The workflow runs; the model reads the task, does the work (through MCP, your PIMS, your files), and comments on the task.
- Visible. Staff see the comment and move the task to Completed; the badge's owner never left NxVET.
A task is the assistant's when its fields.turn_id is set (the voice turn that filed it);
fields.badge names the Clip. Status ids are stable even when a clinic renames the columns.
A. In the clinic: the NxVET Agent app
The NxVET Agent (headless or desktop) already runs in clinics to push recordings into scribes and PIMS. Its workflows run any command on the organization's events:
# 1. Give your LLM tool the NxVET MCP tools (once per tool). --with-key prints the agent's own key.
# mcp-config PRINTS the registration; you run it. For Claude Code it is --scope user, so the
# workflow runs (which start in your home directory) see it, not only the directory you ran it in.
nxvet-headless mcp-config --claude --with-key
# → claude mcp add --scope user --transport http nxvet https://mcp.nx.vet/mcp --header "Authorization: Bearer …"
nxvet-headless mcp-config --codex --with-key # a [mcp_servers.nxvet] block for ~/.codex/config.toml
# 2. A workflow from a template — the command is yours to edit
nxvet-headless workflow-add --name "Confirmed tasks" --template claude-code --on voice_task_confirmed
# runs: claude -p "Read the NxVET event in $NXVET_EVENT_FILE … Do what the task asks using the
# nxvet MCP tools, then add a short comment on the task saying what you did."
# --allowedTools "Read,mcp__nxvet__get_task,mcp__nxvet__list_task_comments,mcp__nxvet__add_task_comment,mcp__nxvet__update_task"
# 3. Try it on a real task now; then leave the agent running (run / agent) — it fires on its own
nxvet-headless workflow-test "Confirmed tasks" --task <taskId>
nxvet-headless workflow-runs
Authorize the tools. Registering the MCP server does not approve its tools: unattended, claude -p
refuses any call that would need a permission prompt. The template's --allowedTools is that approval — the event file
and the four NxVET task tools, no shell, no file edits. A workflow that needs more (say mcp__nxvet__list_labels to read
a transcript) adds it to its own command; mcp__nxvet alone would allow every NxVET tool. Codex's --full-auto
approves its tools itself.
workflow-add --run '<any command>' takes anything: codex exec --full-auto "…",
python3 on-event.py, a PowerShell script. The command runs through the shell, in --cwd,
killed after --timeout seconds (default 600); two at a time, the rest queue; the last 4 KB of output and the
exit code are kept (workflow-runs, GET /workflow-runs on the control API). The agent's own key is
passed to the command only with --with-key.
B. In the cloud: webhooks
- Register an endpoint:
POST /api/organizations/{orgId}/webhookswitheventTypes: ["task_updated", "new_label", …](signing and retries; a free receiver in ~15 minutes: Cloudflare Workers guide). - On
task_updated:GET /api/tasks/{taskId}. Iffields.turn_idis set,statusId !== "needs_confirmation"and the board's status category isopenordone→ a confirmed voice task. Claim it — inserttaskIdinto your own store with a unique key before doing anything; if the insert fails, it was handled already (completion, a comment, a replay). See Idempotency below. - Hand it to your tool on a worker: spawn
claude -p/codex execwith the task JSON, or call a model API with the NxVET MCP server (https://mcp.nx.vet/mcp,Authorization: Beareryour key) as its tool surface. - Close the loop:
POST /api/tasks/{taskId}/commentswith what was done;PATCH /api/tasks/{taskId}to a done status.
The same shape works for recordings (new_label → review the transcript → file follow-ups with
POST /api/task-boards/{boardId}/tasks/bulk) and for the whiteboard (whiteboard_due).
C. Interactive: MCP
Connect Claude, Cursor, Claude Code or any MCP client to https://mcp.nx.vet/mcp (how) and ask.
The voice assistant adds two tools — list_voice_turns and get_voice_turn — next to the task tools:
- "What did the badges ask the assistant for today?" →
list_voice_turns - "Which voice-filed tasks are still waiting for confirmation?" →
list_taskson the Voice assistant board,statusId needs_confirmation - "Confirm the gabapentin prescription Dr. Lee asked for." →
update_tasktoconfirmed(this fires the hand-off above) - "Where does the assistant's time go?" →
timingsMsper turn: speech to text, the model, text to speech
Events
| Event | Webhooks / stream | Agent workflows | Data |
|---|---|---|---|
| Voice task filed (needs confirmation) | task_created | voice_task_filed | taskId, boardId |
| Voice task confirmed | task_updated (check the status transition; see Idempotency) | voice_task_confirmed (once per task) | taskId, boardId |
| Any task | task_created, task_updated, task_deleted, task_comment_added | taskId, boardId[, commentId] | |
| Recordings | new_label, label_updated, label_deleted | labelId | |
| NxHUB conversations | conversation_created, conversation_completed | conversationId, deviceId | |
| Whiteboard | whiteboard_updated, whiteboard_due | patientId, … | |
What your tool receives (agent workflows)
| Where | What |
|---|---|
stdin, $NXVET_EVENT_FILE | one JSON document: { event: {type, taskId?, labelId?, at}, record: {kind: "task" | "label", …} | null, organizationId, organizationName, apiBase, mcp } — the record is fetched for you |
NXVET_EVENT_TYPE | e.g. voice_task_confirmed |
NXVET_TASK_ID, NXVET_LABEL_ID | when the event is about one |
NXVET_API_BASE, NXVET_ORG_ID | https://app.nx.vet and the organization |
NXVET_API_KEY | only with --with-key: the agent's own key, for a tool that calls the REST API itself (an MCP-configured tool does not need it) |
Webhook payloads are documented on the Webhooks page; they carry ids only — fetch the record.
Idempotency & safety
- Confirm once, not on every later change. A confirmed task keeps emitting
task_updated: when staff move it to Completed, edit a field, add a comment. "Status is notneeds_confirmation" is true for all of those, so a workflow keyed on the current status — or on the webhook event id — runs the action again on completion. Act on the transition into a confirmed state and keep a durable claim per task: a row keyed bytaskIdin your own store (a database table with a unique key, a file the worker owns), written before the work starts, so a replay, a restart or two deliveries at once find it. The NxVET Agent does this forvoice_task_confirmed: every handled task id is kept in its settings, none evicted, written before the workflow starts — and the workflow does not run if it could not be written (confirmed → completed, a replay, a restart never fire it again). A comment on the task is a good receipt for people, not a claim: two runs can both read before either writes. - Events repeat. A reconnect replays, webhooks are at-least-once. The claim above is the dedupe; the event id only removes exact duplicates of one delivery.
- A voice task is a request until confirmed. Never act on
task_created/voice_task_filed; act when the status has leftneeds_confirmationfor a status whose category isopenordone— read the board's statuses; an unknown category is not a confirmation;dismissednever is. - Least privilege. Create a scoped API key for the tool (e.g.
tasks:read,tasks:write,nxhub:voice-turns:read); revoke it at app.nx.vet/integrations when the machine or the tool changes. - Say what is allowed in the prompt. The model acts with the clinic's key: read records, create and update tasks, comments, webhooks. Keep destructive steps (deleting, re-generating notes) out of unattended workflows.
- Know where the record goes. The clinic-PC path keeps the runner local, not the data: an LLM tool sends what it reads — the event, the task, a transcript — to its model provider over the network (Claude Code to Anthropic, Codex to OpenAI, under the retention terms of the clinic's plan with that provider; see Claude Code's data-usage documentation). A cloud workflow additionally passes it through wherever your worker runs. Pick the tool and its provider settings with that in mind, scope the key, and keep transcripts out of workflows that do not need them.