Get started
Versioning
The current v2 API and the legacy v1 surface, what Vetnio treats as a breaking change, the OpenAPI document and how to generate a typed client from it.
The Vetnio API has one current version, v2, and a legacy surface, v1, kept for integrations built before v2. Both serve the same endpoints. This guide explains how they differ, which changes Vetnio may make without a new version and how your client should cope with them, and how to use the OpenAPI document to generate a client.
Base URLs
| Version | Base URL | Use it for |
|---|---|---|
| v2 (current) | https://vetnio.com/api/v2 | Every new integration |
| v1 (legacy) | https://vetnio.com/api/v1 | Existing integrations until you move them to v2 |
The version is part of the path; there is no version header.
How v1 differs from v2
v1 and v2 expose the same endpoints, with the same paths after the base URL, the same parameters, the same response bodies and the same error format. The differences are in how requests are handled:
| v2 | v1 | |
|---|---|---|
POST, PUT or PATCH without a body | 415 Missing content-type header | Vetnio adds an empty JSON body for you |
| Error logging | Standard | Vetnio also records each error with the platform request ID (x-vercel-id), which helps support trace a problem |
| Interactive API explorer | https://vetnio.com/api/v2 | https://vetnio.com/api/v1 |
POST/workspaces/{workspaceId}/consultations/{consultationId}/recording-operations while the operation is still running | 200 with the receipt | 202 Accepted with the same receipt, a Location header for the status endpoint and Retry-After: 5. A finished operation returns 200 on both. |
To move an integration from v1 to v2, change the base URL and make sure every POST, PUT and PATCH sends Content-Type: application/json and a JSON body. When an operation has no body fields, such as POST/workspaces/{workspaceId}/consultations/{consultationId}/transcriptions/start, send {}:
curl -X POST https://vetnio.com/api/v2/workspaces/demo-north-clinic/consultations/dfd6e768-51dd-5fcb-a786-c6dffe511d5f/transcriptions/start \
-H "Authorization: Bearer $VETNIO_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'Apart from the status code of a running recording operation, nothing else changes. Keys, IDs and stored data are shared between the two.
What can change in v2
Endpoints in the API reference are a public contract. Within v2, Vetnio does not make breaking changes to them. A breaking change is one that can make a correct client fail:
- removing or renaming an endpoint, a parameter or a response field;
- changing the type or format of a field, or changing what a value means;
- making an optional parameter or body field required, or narrowing the values it accepts;
- changing which key types or roles an endpoint accepts in a way that rejects existing callers.
Additive changes are not breaking and can ship at any time, without notice:
- new endpoints;
- new optional parameters and body fields;
- new fields in responses and in nested objects;
- new values in enumerations, such as a new record type or status;
- new
data.reasontokens and reworded error messages; - a more specific status code for an error that is currently a
500.
Write your client so these do not break it:
- Ignore fields you do not know. Do not validate responses against a closed schema, and do not reject objects with extra properties.
- Handle unknown enum values. Map them to a fallback such as "other" instead of throwing.
- Branch on status codes and
code, not onmessagetext. Messages are for people and can be reworded.data.pathis an internal name; do not depend on it either. - Do not depend on the order of keys in JSON objects, or on the order of lists the reference does not describe as ordered.
- Handle errors by status class. If you handle
4xxand5xxgenerally, a500that becomes a400keeps working.
The OpenAPI document
GET/openapi.json returns the full description of the API as an OpenAPI 3.1 document. It is public: you do not need a key to fetch it.
curl https://vetnio.com/api/v2/openapi.json -o vetnio-openapi.json{
"openapi": "3.1.0",
"info": {
"title": "Vetnio API",
"version": "1.0.0",
"…": "…"
},
"servers": [{ "url": "https://vetnio.com/api/v2" }],
"paths": {
"/workspaces/{workspaceId}/consultations": {
"post": {
"operationId": "consultations-createV1",
"summary": "Create a new consultation",
"security": [{ "Authorization": [] }],
"x-vetnio-api-key-types": ["personal"],
"…": "…"
}
},
"…": "…"
}
}Add ?apiVersion=v1 to get the v1 description of the same endpoints, with v1's base URL in servers. info.version does not change when endpoints are added, so do not use it to detect changes; compare documents instead.
Every operation carries x-vetnio-api-key-types, the key types it accepts: personal, organization and workspace (clinic). The reference shows the same list as Accepts. See Authentication.
Generate a client
Any OpenAPI 3.1 tool can generate types or a client from the document. For TypeScript, openapi-typescript generates types and openapi-fetch provides a small typed client:
npx openapi-typescript https://vetnio.com/api/v2/openapi.json -o src/vetnio.d.ts
npm install openapi-fetchimport createClient, { type Middleware } from "openapi-fetch";
import type { paths } from "./vetnio";
// v2 needs a JSON body on every POST, PUT and PATCH, including operations
// whose description has no body fields.
const sendEmptyJsonBody: Middleware = {
onRequest({ request }) {
if (!["POST", "PUT", "PATCH"].includes(request.method) || request.body !== null) return undefined;
const headers = new Headers(request.headers);
headers.set("Content-Type", "application/json");
return new Request(request, { body: "{}", headers });
},
};
const vetnio = createClient<paths>({
baseUrl: "https://vetnio.com/api/v2",
headers: { Authorization: `Bearer ${process.env.VETNIO_API_KEY}` },
});
vetnio.use(sendEmptyJsonBody);
const { data, error } = await vetnio.GET("/workspaces/{workspaceId}/consultations/{consultationId}", {
params: { path: { workspaceId: "demo-north-clinic", consultationId: "dfd6e768-51dd-5fcb-a786-c6dffe511d5f" } },
});
if (error) throw new Error(error.message);
console.log(data.transcriptionStatus);Whatever generator you use, check these points:
- Empty bodies. POST
/workspaces/{workspaceId}/consultations/{consultationId}/transcriptions/start, POST/workspaces/{workspaceId}/consultations/{consultationId}/process-recordingsand POST/workspaces/{workspaceId}/consultations/{consultationId}/uploaded-files/{fileName}/finalizedeclare no request body, so generated clients send none and v2 answers415. Add{}as in the middleware above. - Error bodies. The document describes errors with
message,codeandissuesonly. Real error bodies also containdata(see Errors), so turn off strict validation of error responses. - Timestamps are ISO-8601 strings. Some are marked
format: date-timeand some are plain strings; convert them yourself rather than relying on the generator. - Operation IDs such as
consultations-createV1come from Vetnio's internal names and some carry a historicalV1suffix. They identify operations in both v1 and v2. - Timeouts. Set the HTTP timeout above 10 minutes for endpoints that generate documents; see Limits.
Keep the document you generated from in your repository. When you regenerate, review the diff: additive changes appear as new paths, parameters and properties.
Next steps
- Authentication: which keys an operation accepts.
- Errors: the error format your client should parse.
- The OpenAPI document in the reference.
Endpoints used in this guide
- GET
/openapi.jsonGet the OpenAPI document - POST
/workspaces/{workspaceId}/consultationsCreate a new consultation - POST
/workspaces/{workspaceId}/consultations/{consultationId}/transcriptions/startStart transcription - POST
/workspaces/{workspaceId}/consultations/{consultationId}/process-recordingsProcess recordings - POST
/workspaces/{workspaceId}/consultations/{consultationId}/uploaded-files/{fileName}/finalizeFinalize a file upload and trigger analysis - POST
/workspaces/{workspaceId}/consultations/{consultationId}/recording-operationsDurably accept an idempotent recording submission
Last reviewed against the API on 2026-10-26.