Example Flows
Common API usage patterns and integration workflows
Last updated: February 20, 2026
Overview
This page demonstrates common integration flows using the NxVET API. Each example shows the sequence of API calls, request parameters, and how to work with the response data.
All examples use the following base configuration:
# Set your credentials
API_KEY="nxvet_sk_YOUR_API_KEY"
ORG_ID="YOUR_ORGANIZATION_ID"
BASE_URL="https://app.nx.vet/api"
const API_KEY = process.env.NXVET_API_KEY;
const ORG_ID = process.env.NXVET_ORG_ID;
const BASE_URL = 'https://app.nx.vet/api';
const headers = {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
};
import os
import requests
API_KEY = os.environ['NXVET_API_KEY']
ORG_ID = os.environ['NXVET_ORG_ID']
BASE_URL = 'https://app.nx.vet/api'
headers = {
'Authorization': f'Bearer {API_KEY}',
'Content-Type': 'application/json'
}
List & Filter Labels
Fetch consultation records (labels) for your organization with pagination and filtering.
Step 1 — List devices (for filtering)
Optionally fetch the list of devices to enable device-based filtering:
curl -H "Authorization: Bearer $API_KEY" \
"$BASE_URL/devices?organizationId=$ORG_ID&limit=200"
const res = await fetch(
`${BASE_URL}/devices?organizationId=${ORG_ID}&limit=200`,
{ headers }
);
const devices = await res.json();
// devices = [{ value: "device-id", displayName: "Room 1", serial: "NX-1234" }, ...]
res = requests.get(
f'{BASE_URL}/devices',
params={'organizationId': ORG_ID, 'limit': 200},
headers=headers
)
devices = res.json()
# devices = [{"value": "device-id", "displayName": "Room 1", "serial": "NX-1234"}, ...]
Step 2 — Fetch labels with pagination
List labels sorted by date, with optional filters for record type, patient search, and user.
# Paginated list with filters
curl -H "Authorization: Bearer $API_KEY" \
"$BASE_URL/organizations/$ORG_ID/labels?\
limit=10&\
offset=0&\
sortingProperty=FromTime&\
isSortingDescending=true&\
patientSearch=Luna&\
types=ClinicConversation,NxHubBatch"
const params = new URLSearchParams({
limit: 10,
offset: 0,
sortingProperty: 'FromTime',
isSortingDescending: true,
patientSearch: 'Luna',
types: 'ClinicConversation,NxHubBatch'
});
const res = await fetch(
`${BASE_URL}/organizations/${ORG_ID}/labels?${params}`,
{ headers }
);
// Total count is in the response header
const totalCount = res.headers.get('X-Total-Count');
const labels = await res.json();
console.log(`Showing ${labels.length} of ${totalCount} labels`);
res = requests.get(
f'{BASE_URL}/organizations/{ORG_ID}/labels',
params={
'limit': 10,
'offset': 0,
'sortingProperty': 'FromTime',
'isSortingDescending': True,
'patientSearch': 'Luna',
'types': 'ClinicConversation,NxHubBatch'
},
headers=headers
)
total_count = res.headers.get('X-Total-Count')
labels = res.json()
print(f'Showing {len(labels)} of {total_count} labels')
Available sorting properties: FromTime, Type, Patient
Available record types:
ClinicConversation— Live clinic recordingNxHubBatch— NxHub ambient recordingDictationAudio— DictationDocumentSoap— Document uploadPhoneCallAudio— Phone call recordingButtonRecording,AudioButtonRecording— Button-triggered recordingsAggregateLabel— Combined medical record
Get Label Details
Retrieve full details for a specific label, including transcript, notes, and patient data.
LABEL_ID="019e1234-5678-7000-abcd-123456789abc"
curl -H "Authorization: Bearer $API_KEY" \
"$BASE_URL/labels/$LABEL_ID"
const labelId = '019e1234-5678-7000-abcd-123456789abc';
const res = await fetch(`${BASE_URL}/labels/${labelId}`, { headers });
const label = await res.json();
// Access patient info
console.log(`Patient: ${label.patient?.name}`);
console.log(`Type: ${label.type}`);
console.log(`Duration: ${label.fromTime} — ${label.toTime}`);
// Access transcript and notes
for (const note of label.ownedPatientNotes) {
const content = JSON.parse(note.content);
// Transcript text
if (content.english_soap) {
console.log('Transcript:', content.english_soap);
}
// Clinical note
if (content.clinical_note) {
console.log('Clinical Note:', content.clinical_note);
}
}
import json
label_id = '019e1234-5678-7000-abcd-123456789abc'
res = requests.get(f'{BASE_URL}/labels/{label_id}', headers=headers)
label = res.json()
# Access patient info
print(f"Patient: {label.get('patient', {}).get('name')}")
print(f"Type: {label['type']}")
# Access transcript and notes
for note in label.get('ownedPatientNotes', []):
content = json.loads(note['content'])
if 'english_soap' in content:
print(f"Transcript: {content['english_soap']}")
if 'clinical_note' in content:
print(f"Clinical Note: {content['clinical_note']}")
Response structure
The label detail response includes:
id,type,fromTime,toTime— Basic label metadatapatient— Assigned patient object (id,name)ownedPatientNotes[]— Array of notes. Each note has acontentfield (JSON string) containing transcript, clinical notes, and metadatafriendlyName,deviceSerial— Device info
Assign Patient to Label
Associate a patient with a consultation label.
LABEL_ID="019e1234-5678-7000-abcd-123456789abc"
PATIENT_ID="019e5678-abcd-7000-1234-abcdef012345"
curl -X POST -H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d "{\"patientId\": \"$PATIENT_ID\"}" \
"$BASE_URL/labels/$LABEL_ID/assign"
const labelId = '019e1234-5678-7000-abcd-123456789abc';
const patientId = '019e5678-abcd-7000-1234-abcdef012345';
const res = await fetch(`${BASE_URL}/labels/${labelId}/assign`, {
method: 'POST',
headers,
body: JSON.stringify({ patientId })
});
if (res.ok) {
console.log('Patient assigned successfully');
}
label_id = '019e1234-5678-7000-abcd-123456789abc'
patient_id = '019e5678-abcd-7000-1234-abcdef012345'
res = requests.post(
f'{BASE_URL}/labels/{label_id}/assign',
json={'patientId': patient_id},
headers=headers
)
if res.ok:
print('Patient assigned successfully')
Generate Medical Record
Combine multiple labels into a single medical record using a template. This is useful for creating comprehensive records from several consultation sessions.
Step 1 — Fetch available templates
curl -H "Authorization: Bearer $API_KEY" \
"$BASE_URL/organizations/$ORG_ID/medical-templates"
const res = await fetch(
`${BASE_URL}/organizations/${ORG_ID}/medical-templates`,
{ headers }
);
const templates = await res.json();
// templates = [{ id: "...", name: "Standard SOAP", content: "..." }, ...]
Step 2 — Aggregate labels
Combine up to 5 labels into a medical record:
curl -X POST -H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"LabelIds": [
"019e1111-1111-7000-aaaa-111111111111",
"019e2222-2222-7000-bbbb-222222222222"
],
"PatientId": "019e5678-abcd-7000-1234-abcdef012345",
"MedicalTemplateId": "template-id-here"
}' \
"$BASE_URL/labels/aggregate"
const res = await fetch(`${BASE_URL}/labels/aggregate`, {
method: 'POST',
headers,
body: JSON.stringify({
LabelIds: [
'019e1111-1111-7000-aaaa-111111111111',
'019e2222-2222-7000-bbbb-222222222222'
],
PatientId: '019e5678-abcd-7000-1234-abcdef012345',
MedicalTemplateId: 'template-id-here'
})
});
const newLabel = await res.json();
console.log(`Created aggregate label: ${newLabel.id}`);
Step 3 — Trigger report regeneration (optional)
Reprocess a label with a different template. This is a fire-and-forget operation — both 200 and 504 responses indicate the job was triggered successfully.
LABEL_ID="019e1234-5678-7000-abcd-123456789abc"
TEMPLATE_ID="template-id-here"
curl -X POST -H "Authorization: Bearer $API_KEY" \
"$BASE_URL/labels/$LABEL_ID/lambda?medicalTemplateId=$TEMPLATE_ID"
# Note: 504 Gateway Timeout is expected — the job runs asynchronously
const labelId = '019e1234-5678-7000-abcd-123456789abc';
const templateId = 'template-id-here';
try {
await fetch(
`${BASE_URL}/labels/${labelId}/lambda?medicalTemplateId=${templateId}`,
{ method: 'POST', headers }
);
} catch (e) {
// 504 is expected — the job is processing asynchronously
}
// Use webhooks to know when the label is updated (label_updated event)
label_updated webhook events to get notified when the regenerated report is ready.
Manage Conversations
List and inspect NxHub ambient recording conversations with token-based pagination.
Step 1 — List conversations
# List active conversations
curl -H "Authorization: Bearer $API_KEY" \
"$BASE_URL/nxhub/conversations?\
organizationId=$ORG_ID&\
status=ACTIVE&\
pageSize=20"
# Fetch next page using the token from the previous response
curl -H "Authorization: Bearer $API_KEY" \
"$BASE_URL/nxhub/conversations?\
organizationId=$ORG_ID&\
pageSize=20&\
pageToken=NEXT_PAGE_TOKEN"
async function listConversations(status = null, pageToken = null) {
const params = new URLSearchParams({
organizationId: ORG_ID,
pageSize: 20
});
if (status) params.set('status', status);
if (pageToken) params.set('pageToken', pageToken);
const res = await fetch(
`${BASE_URL}/nxhub/conversations?${params}`,
{ headers }
);
return res.json();
}
// List active conversations
const result = await listConversations('ACTIVE');
console.log(`Found ${result.items.length} conversations`);
// Paginate if more results exist
if (result.hasMore) {
const nextPage = await listConversations('ACTIVE', result.nextPageToken);
}
def list_conversations(status=None, page_token=None):
params = {'organizationId': ORG_ID, 'pageSize': 20}
if status:
params['status'] = status
if page_token:
params['pageToken'] = page_token
res = requests.get(
f'{BASE_URL}/nxhub/conversations',
params=params,
headers=headers
)
return res.json()
# List active conversations
result = list_conversations(status='ACTIVE')
print(f"Found {len(result['items'])} conversations")
# Paginate
if result.get('hasMore'):
next_page = list_conversations('ACTIVE', result['nextPageToken'])
Conversation statuses: ACTIVE, COMPLETED, PROCESSING, UPLOADED, FAILED, FILTERED
Step 2 — Get conversation details
DEVICE_ID="device-id-here"
CONVERSATION_ID="conversation-id-here"
curl -H "Authorization: Bearer $API_KEY" \
"$BASE_URL/nxhub/conversations/$DEVICE_ID/$CONVERSATION_ID"
const deviceId = 'device-id-here';
const conversationId = 'conversation-id-here';
const res = await fetch(
`${BASE_URL}/nxhub/conversations/${deviceId}/${conversationId}`,
{ headers }
);
const conversation = await res.json();
console.log(`Status: ${conversation.status}`);
console.log(`Speech: ${conversation.speechMinuteCount} min`);
console.log(`Transcript: ${conversation.transcriptText || '(processing)'}`);
Create a Conversation
Create a new NxHub conversation from a time range on a device. This flow involves previewing available audio, creating the conversation, and completing it.
Step 1 — View the device timeline
Fetch the timeline to see when speech was detected (max 6-hour window):
DEVICE_ID="device-id-here"
# 3-hour window: now minus 3 hours to now (in milliseconds)
END_MS=$(date +%s000)
START_MS=$((END_MS - 10800000))
curl -H "Authorization: Bearer $API_KEY" \
"$BASE_URL/nxhub/conversations/timeline/$DEVICE_ID?\
startMs=$START_MS&\
endMs=$END_MS"
const deviceId = 'device-id-here';
const endMs = Date.now();
const startMs = endMs - (3 * 60 * 60 * 1000); // 3 hours ago
const res = await fetch(
`${BASE_URL}/nxhub/conversations/timeline/${deviceId}?startMs=${startMs}&endMs=${endMs}`,
{ headers }
);
const timeline = await res.json();
// timeline.minutes[] contains per-minute VAD (voice activity detection) data
for (const minute of timeline.minutes) {
if (!minute.vadIsSilent) {
console.log(`Speech at ${new Date(minute.minuteStartMs).toLocaleTimeString()}`);
console.log(` Duration: ${minute.vadSpeechDurationMs}ms`);
if (minute.transcriptText) {
console.log(` Text: ${minute.transcriptText}`);
}
}
}
Step 2 — Preview the time range
Check the audio content before creating (max 90-minute range):
# Select a 30-minute window with speech activity
curl -H "Authorization: Bearer $API_KEY" \
"$BASE_URL/nxhub/conversations/create/preview?\
deviceId=$DEVICE_ID&\
startMs=$START_MS&\
endMs=$END_MS"
const previewRes = await fetch(
`${BASE_URL}/nxhub/conversations/create/preview?deviceId=${deviceId}&startMs=${startMs}&endMs=${endMs}`,
{ headers }
);
const preview = await previewRes.json();
console.log(`Total: ${preview.totalMinutes} min`);
console.log(`Speech: ${preview.speechMinutes} min`);
console.log(`Silent: ${preview.silentMinutes} min`);
Step 3 — Create the conversation
curl -X POST -H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d "{
\"deviceId\": \"$DEVICE_ID\",
\"startMs\": $START_MS,
\"endMs\": $END_MS
}" \
"$BASE_URL/nxhub/conversations/create"
const res = await fetch(`${BASE_URL}/nxhub/conversations/create`, {
method: 'POST',
headers,
body: JSON.stringify({ deviceId, startMs, endMs })
});
const result = await res.json();
if (result.success) {
console.log(`Created conversation: ${result.conversation.conversationId}`);
} else {
console.log(`Error: ${result.message}`);
}
Step 4 — Complete the conversation
When the conversation is ready, mark it as complete to trigger processing:
curl -X POST -H "Authorization: Bearer $API_KEY" \
"$BASE_URL/nxhub/conversations/$DEVICE_ID/$CONVERSATION_ID/complete"
const res = await fetch(
`${BASE_URL}/nxhub/conversations/${deviceId}/${conversationId}/complete`,
{ method: 'POST', headers }
);
const result = await res.json();
if (result.success) {
console.log('Conversation completed — processing will begin');
// Subscribe to conversation_completed webhook to know when it's done
}
conversation_completed webhook events to get notified when processing finishes and the label is created.
Webhook-Driven Sync
Use webhooks to keep your system in sync with NxVET in real-time. When an event occurs, fetch the relevant data from the API.
Example: Sync new labels to your system
const express = require('express');
const crypto = require('crypto');
const app = express();
const WEBHOOK_SECRET = process.env.NXVET_WEBHOOK_SECRET; // whsec_...
// Parse raw body for signature verification
app.post('/webhooks/nxvet', express.raw({ type: '*/*' }), async (req, res) => {
// 1. Verify signature
const signature = req.headers['x-nervex-signature'];
const expected = 'sha256=' + crypto
.createHmac('sha256', WEBHOOK_SECRET)
.update(req.body)
.digest('hex');
if (signature !== expected) {
return res.status(401).send('Invalid signature');
}
// 2. Process the event
const event = JSON.parse(req.body);
const deliveryId = req.headers['x-nervex-delivery'];
switch (event.type) {
case 'new_label': {
// Fetch the full label details
const label = await fetch(
`${BASE_URL}/labels/${event.data.labelId}`,
{ headers }
).then(r => r.json());
console.log(`New label: ${label.id}, patient: ${label.patient?.name}`);
// Save to your database...
break;
}
case 'label_updated': {
// Re-fetch the label to get updated content
const label = await fetch(
`${BASE_URL}/labels/${event.data.labelId}`,
{ headers }
).then(r => r.json());
console.log(`Label updated: ${label.id}`);
// Update in your database...
break;
}
case 'conversation_completed': {
// Fetch conversation details
const conv = await fetch(
`${BASE_URL}/nxhub/conversations/${event.data.deviceId}/${event.data.conversationId}`,
{ headers }
).then(r => r.json());
console.log(`Conversation completed: ${conv.conversationId}`);
break;
}
}
// 3. Return 200 to acknowledge receipt
res.status(200).send('OK');
});
app.listen(3000);
import hmac
import hashlib
from flask import Flask, request
app = Flask(__name__)
WEBHOOK_SECRET = os.environ['NXVET_WEBHOOK_SECRET'] # whsec_...
@app.route('/webhooks/nxvet', methods=['POST'])
def handle_webhook():
# 1. Verify signature
signature = request.headers.get('X-NerveX-Signature', '')
expected = 'sha256=' + hmac.new(
WEBHOOK_SECRET.encode(),
request.data,
hashlib.sha256
).hexdigest()
if not hmac.compare_digest(signature, expected):
return 'Invalid signature', 401
# 2. Process the event
event = request.get_json()
delivery_id = request.headers.get('X-NerveX-Delivery')
if event['type'] == 'new_label':
label = requests.get(
f"{BASE_URL}/labels/{event['data']['labelId']}",
headers=headers
).json()
print(f"New label: {label['id']}, patient: {label.get('patient', {}).get('name')}")
# Save to your database...
elif event['type'] == 'conversation_completed':
conv = requests.get(
f"{BASE_URL}/nxhub/conversations/{event['data']['deviceId']}/{event['data']['conversationId']}",
headers=headers
).json()
print(f"Conversation completed: {conv['conversationId']}")
# 3. Return 200 to acknowledge receipt
return 'OK', 200
Webhook + API pattern summary
| Webhook Event | Follow-up API Call | Use Case |
|---|---|---|
new_label |
GET /api/labels/{labelId} |
Import new consultation into your system |
label_updated |
GET /api/labels/{labelId} |
Sync updated transcript or notes |
label_deleted |
— | Remove consultation from your system |
conversation_created |
GET /api/nxhub/conversations/{deviceId}/{conversationId} |
Track new ambient recording |
conversation_completed |
GET /api/nxhub/conversations/{deviceId}/{conversationId} |
Retrieve completed conversation with transcript |