API reference · Data export
Read-only endpoints for BI tools, dashboards and data warehouses: the key you are using, your organization with its clinics and people, and usage statistics. They never return patient records, notes, transcripts, audio or chat content. See Export usage statistics for a walkthrough.
The organization or workspace API key that authenticated this request, with its scope, lifecycle and usage. The secret key value is never returned.
namestringrequiredLabel an organization admin gave the key when creating it under Organization → API. A rotated key keeps the name of the key it replaced.
organizationIdstringrequiredIdentifier of the organization the key belongs to. Use it as {organizationId} in the /organizations/{organizationId}/… paths; any other organization ID returns 403.
scopeobjectrequiredThe organization or single clinic the key is limited to.
typeenumrequiredWhat the key can reach:
organization — every clinic in the organization. Keys start with sk_org_.workspace — a single clinic (workspace). Keys start with sk_ws_ and only ever see that clinic's data.idstringrequiredThe organization ID when type is organization, or the workspace (clinic) ID when type is workspace.
statusenumrequiredLifecycle state of the key:
active — valid and has no expiry date.expiring — valid until expiresAt. A key replaced through rotation is expiring during its grace period.expired — expiresAt has passed; the key no longer authenticates.revoked — an admin revoked the key; it no longer authenticates.
Because expired and revoked keys are rejected with 401 before this endpoint runs, a successful response is always active or expiring.createdAtstringrequiredWhen the key was created, as an ISO-8601 UTC timestamp.
expiresAtstringrequirednullableWhen the key stops working (ISO-8601 UTC), or null if it never expires. Rotating a key moves the old key's expiry to at most 24 hours after the rotation.
lastUsedAtstringrequirednullableApproximate time of the key's most recent authenticated request (ISO-8601 UTC), or null if it has not been recorded yet. Refreshed at most once every 5 minutes, so it may lag and may not include the current request.
limitsobjectrequiredRate limits that apply to every organization and workspace key.
requestsPerMinutenumberrequiredMaximum requests per fixed UTC minute. The next request returns 429 TOO_MANY_REQUESTS with the number of seconds to wait in message.
requestsPerDaynumberrequiredMaximum requests per UTC day; the counter resets at 00:00 UTC. Exceeding it returns 429 until the reset.
usageobjectrequiredRequest counts recorded by the key's rate limiter.
requestsTodaynumberrequiredRequests counted for this key since 00:00 UTC today, including the current one. Every request to an endpoint the key may call is counted, even if it then fails validation or is rate limited.
requestsLast30DaysnumberrequiredRequests counted for this key over the last 30 UTC days, today included.
dailyarray of objectsrequiredOne entry per UTC day in the last 30 days on which the key made at least one request, oldest first. Days without requests are omitted rather than returned as zero.
daystringrequiredUTC calendar day, formatted YYYY-MM-DD.
requestsnumberrequiredRequests counted for this key on that day.
{
"name": "Data warehouse sync",
"organizationId": "3f6c2a1e-8b4d-4c7a-9e2f-5d1b7a9c0e42",
"scope": {
"type": "organization",
"id": "3f6c2a1e-8b4d-4c7a-9e2f-5d1b7a9c0e42"
},
"status": "active",
"createdAt": "2026-09-01T08:15:00.000Z",
"expiresAt": null,
"lastUsedAt": "2026-09-28T06:00:12.000Z",
"limits": {
"requestsPerMinute": 60,
"requestsPerDay": 10000
},
"usage": {
"requestsToday": 42,
"requestsLast30Days": 1250,
"daily": [
{
"day": "2026-09-28",
"requests": 37
}
]
}
}One page of the usage table: the same table as the in-app usage CSV export, as JSON.
headersarray of stringsrequiredColumn titles in display form (for example Clinic Name, Consultations), in the order of each row's cells. For grain=vet and grain=clinic the first column is Date.
rowsarray of array of string | numberssrequiredTable rows, one cell per header. Cells are strings, numbers or null (not applicable or unknown). Dates are YYYY-MM-DD strings (the next and last payment dates are full ISO-8601 timestamps), consultation start and end times are HH:mm in the clinic's local time, flags are Yes or No, amounts are rounded to 2 decimals and Draft Kept (%) is a percentage from 0 to 100.
startstringrequiredFirst day covered, YYYY-MM-DD (UTC).
endstringrequiredLast day covered, YYYY-MM-DD (UTC).
totalRowsnumberrequiredNumber of rows across all pages. For grains other than consultation it equals the number of rows returned.
pagenumberrequiredThe page returned. Always 1 unless grain=consultation.
totalPagesnumberrequiredOnly the consultation grain spans several pages; the others always return 1.
{
"headers": [
"Date",
"Clinic ID",
"Clinic Name",
"Consultations"
],
"rows": [
[
"2026-09-01",
"furunculotic-loud-chihuahua",
"Riverside Animal Clinic",
412
]
],
"start": "2026-09-01",
"end": "2026-09-30",
"totalRows": 24,
"page": 1,
"totalPages": 1
}GET/api-key
Returns the calling key's organization, scope, status, limits and request counts for today and the last 30 days. Use it as a first call to confirm a key works and to look up the organizationId for the other data-export paths. Only organization (sk_org_…) and workspace (sk_ws_…) keys are accepted: a personal API key or a browser session gets 400 BAD_REQUEST. The call itself counts toward the key's rate limits.
AcceptsOrganization keyClinic key
namestringrequiredLabel an organization admin gave the key when creating it under Organization → API. A rotated key keeps the name of the key it replaced.
organizationIdstringrequiredIdentifier of the organization the key belongs to. Use it as {organizationId} in the /organizations/{organizationId}/… paths; any other organization ID returns 403.
scopeobjectrequiredThe organization or single clinic the key is limited to.
typeenumrequiredWhat the key can reach:
organization — every clinic in the organization. Keys start with sk_org_.workspace — a single clinic (workspace). Keys start with sk_ws_ and only ever see that clinic's data.idstringrequiredThe organization ID when type is organization, or the workspace (clinic) ID when type is workspace.
statusenumrequiredLifecycle state of the key:
active — valid and has no expiry date.expiring — valid until expiresAt. A key replaced through rotation is expiring during its grace period.expired — expiresAt has passed; the key no longer authenticates.revoked — an admin revoked the key; it no longer authenticates.
Because expired and revoked keys are rejected with 401 before this endpoint runs, a successful response is always active or expiring.createdAtstringrequiredWhen the key was created, as an ISO-8601 UTC timestamp.
expiresAtstringrequirednullableWhen the key stops working (ISO-8601 UTC), or null if it never expires. Rotating a key moves the old key's expiry to at most 24 hours after the rotation.
lastUsedAtstringrequirednullableApproximate time of the key's most recent authenticated request (ISO-8601 UTC), or null if it has not been recorded yet. Refreshed at most once every 5 minutes, so it may lag and may not include the current request.
limitsobjectrequiredRate limits that apply to every organization and workspace key.
requestsPerMinutenumberrequiredMaximum requests per fixed UTC minute. The next request returns 429 TOO_MANY_REQUESTS with the number of seconds to wait in message.
requestsPerDaynumberrequiredMaximum requests per UTC day; the counter resets at 00:00 UTC. Exceeding it returns 429 until the reset.
usageobjectrequiredRequest counts recorded by the key's rate limiter.
requestsTodaynumberrequiredRequests counted for this key since 00:00 UTC today, including the current one. Every request to an endpoint the key may call is counted, even if it then fails validation or is rate limited.
requestsLast30DaysnumberrequiredRequests counted for this key over the last 30 UTC days, today included.
dailyarray of objectsrequiredOne entry per UTC day in the last 30 days on which the key made at least one request, oldest first. Days without requests are omitted rather than returned as zero.
daystringrequiredUTC calendar day, formatted YYYY-MM-DD.
requestsnumberrequiredRequests counted for this key on that day.
curl "https://vetnio.com/api/v2/api-key" \
-H "Authorization: Bearer $VETNIO_API_KEY"{
"name": "Data warehouse sync",
"organizationId": "3f6c2a1e-8b4d-4c7a-9e2f-5d1b7a9c0e42",
"scope": {
"type": "organization",
"id": "3f6c2a1e-8b4d-4c7a-9e2f-5d1b7a9c0e42"
},
"status": "active",
"createdAt": "2026-09-01T08:15:00.000Z",
"expiresAt": null,
"lastUsedAt": "2026-09-28T06:00:12.000Z",
"limits": {
"requestsPerMinute": 60,
"requestsPerDay": 10000
},
"usage": {
"requestsToday": 42,
"requestsLast30Days": 1250,
"daily": [
{
"day": "2026-09-28",
"requests": 37
}
]
}
}GET/organizations/{organizationId}
Returns the organization, its clinics, its people (organization members and clinic staff, one row per person) and the names of its organization-level templates. Use it to look up clinic IDs for the usage export's workspaceIds and to map user IDs in usage rows to people. With a workspace key, workspaces and clinic staff are limited to that clinic, but organization members and organization templates are still listed. Read-only; every call is recorded in the audit log. Requires organization admin rights: an organization or workspace API key, or a personal key or session of an organization admin.
AcceptsPersonal keyOrganization keyClinic key
organizationIdstringrequiredIdentifier of the organization (a UUID). With an organization or workspace key, use the organizationId returned by GET /api-key; any other organization returns 403.
organizationobjectrequiredThe organization being exported.
idstringrequiredOrganization ID (UUID).
namestringrequiredOrganization display name.
workspacesarray of objectsrequiredClinics in scope, sorted by name. Clinics that are locked or being deleted or merged are left out here, although the usage export still reports them. A workspace key sees only its own clinic.
idstringrequiredWorkspace (clinic) ID. Use it in the usage export's workspaceIds filter.
namestringrequiredClinic name, or the clinic ID when no name has been set.
citystringrequirednullableCity of the clinic's address, or null if not set.
countryCodestringrequirednullableTwo-letter country code of the clinic as stored (case as entered), or null if not set.
createdAtstringrequiredWhen the clinic was created in Vetnio (ISO-8601 UTC).
membersarray of objectsrequiredOrganization members and staff of the clinics in scope, merged to one row per person and sorted by email. Organization members are always included, even for a workspace key.
userIdstringrequiredUser ID (UUID). Matches the Vet ID column of the usage export.
namestringrequirednullableThe person's display name, or null if they have not set one.
emailstringrequiredThe person's login email address.
organizationRoleenumrequirednullableRole in the organization, or null for clinic-only staff:
member — belongs to the organization without admin rights.admin — organization administrator.superadmin — organization administrator with the highest role.
Both admin and superadmin have organization admin rights in the API.workspacesarray of objectsrequiredThe person's direct memberships in the clinics in scope. Empty for organization members who do not belong to any of those clinics.
workspaceIdstringrequiredID of a clinic in scope that the person belongs to directly.
roleenumrequiredThe person's role in this clinic:
member — clinician.admin — clinic administrator.superadmin — clinic administrator who can also delete the clinic and manage other superadmins.templatesarray of objectsrequiredOrganization-level templates shared to its clinics, oldest first. Names and metadata only; download bodies with GET /organizations/{organizationId}/data-export/templates.
idstringrequiredOrganization template ID (UUID).
namestringrequiredTemplate name.
typeenumrequiredWhat the template produces:
clinical_note — the clinical note (medical record entry) for a consultation.referral — a referral letter to another clinic or specialist.client_communication — a message to the pet owner.createdBystringrequiredUser ID (UUID) of the organization admin who created the template.
createdAtstringrequiredWhen the template was created (ISO-8601 UTC).
curl "https://vetnio.com/api/v2/organizations/3f6c2a1e-8b4d-4c7a-9e2f-5d1b7a9c0e42" \
-H "Authorization: Bearer $VETNIO_API_KEY"{
"organization": {
"id": "3f6c2a1e-8b4d-4c7a-9e2f-5d1b7a9c0e42",
"name": "Riverside Veterinary Group"
},
"workspaces": [
{
"id": "furunculotic-loud-chihuahua",
"name": "Riverside Animal Clinic",
"city": "Uppsala",
"countryCode": "se",
"createdAt": "2025-11-03T09:12:44.000Z"
}
],
"members": [
{
"userId": "0f8fad5b-d9cb-469f-a165-70867728950e",
"name": "Dr. Sam Lee",
"email": "sam.lee@riverside-vets.example",
"organizationRole": "member",
"workspaces": [
{
"workspaceId": "furunculotic-loud-chihuahua",
"role": "member"
}
]
}
],
"templates": [
{
"id": "2a9b8c7d-6e5f-4a3b-9c1d-0e2f4a6b8c0d",
"name": "Group clinical note",
"type": "clinical_note",
"createdBy": "0f8fad5b-d9cb-469f-a165-70867728950e",
"createdAt": "2026-02-10T13:00:00.000Z"
}
]
}GET/organizations/{organizationId}/data-export/usage
Returns the same table as Organization → Export in the app, as JSON: headers plus one array of cells per row. Choose the period (days, or startDate/endDate), the row level (grain), the time bucket (timePeriod), clinics (workspaceIds) and columns. Only grain=consultation is paginated. A workspace key only ever gets its own clinic's rows. Read-only; every call is recorded in the audit log. Requests that include note-quality columns run an extra analytics query and are limited per key per day. Requires organization admin rights, like the overview endpoint.
AcceptsPersonal keyOrganization keyClinic key
organizationIdstringrequiredIdentifier of the organization (a UUID). With an organization or workspace key, use the organizationId returned by GET /api-key; any other organization returns 403.
workspaceIdsstringoptionalComma-separated workspace (clinic) IDs to include, for example furunculotic-loud-chihuahua,brave-quick-heron. Every ID must belong to the organization (or, for a workspace key, be the key's own clinic), otherwise the request fails with 400. Default: every clinic attached to the organization, including locked clinics.
columnsstringoptionalComma-separated column keys, for example workspaceName,consultationCount,recordingHours. Columns are always returned in the export's fixed order, not the order listed. Columns that do not apply to the chosen grain are dropped silently: vet columns need grain=vet or consultation, consultation columns need grain=consultation, and at grain=consultation the aggregate columns are not available. An unknown key fails with 400 and the message lists every valid key. Default: every column except the note-quality ones (avgReadingSeconds, avgEditingSeconds, acceptanceRate, draftOutcome), which are only included when listed and are limited to 20 requests per key per day. The Export dialog's “Use via API” button builds this value for you.
startDatestringoptionalFirst day of a fixed date range, YYYY-MM-DD (UTC). When set, days is ignored. The range may span at most 366 days; longer ranges fail with 400.
endDatestringoptionalLast day of the fixed date range, inclusive, YYYY-MM-DD (UTC). Only used together with startDate; without startDate it is ignored. Default: up to the current moment.
daysintegeroptionalLength of a rolling window ending now, in days (1-366, default 30). Today counts as the first day, so days=7 covers today and the 6 days before it, starting at midnight UTC. Ignored when startDate is set.
≥ 1 · ≤ 366
timePeriodenumoptionalSize of the time bucket each row covers when grain is vet or clinic; ignored for snapshot and consultation. Buckets are UTC. The first column (Date) holds the bucket's first day as YYYY-MM-DD, clamped to the start of the range:
daily — one bucket per calendar day.weekly — one bucket per week, starting Monday.monthly — one bucket per calendar month.yearly — one bucket per calendar year.Default monthly
grainenumoptionalWhat one row represents:
vet — one vet in one clinic for one time bucket; only vets with activity get rows, and a vet working in two clinics gets a row for each.clinic — one clinic for one time bucket; every clinic in scope gets a row (zero-filled) for each bucket with any activity.snapshot — one row per clinic for the whole period, without a Date column.consultation — one row per consultation, oldest first, including deleted ones (flagged in the Deleted column). The only paginated grain.Default vet
pageintegeroptionalPage number, starting at 1. Only grain=consultation spans several pages; other grains always return everything on page 1. Pages that start beyond row 50,000 are rejected with 400: split the date range instead.
Default 1 · ≥ 1 · ≤ 9007199254740991
pageSizeintegeroptionalRows per page for grain=consultation, 1-5000. Ignored for the other grains.
Default 1000 · ≥ 1 · ≤ 5000
headersarray of stringsrequiredColumn titles in display form (for example Clinic Name, Consultations), in the order of each row's cells. For grain=vet and grain=clinic the first column is Date.
rowsarray of array of string | numberssrequiredTable rows, one cell per header. Cells are strings, numbers or null (not applicable or unknown). Dates are YYYY-MM-DD strings (the next and last payment dates are full ISO-8601 timestamps), consultation start and end times are HH:mm in the clinic's local time, flags are Yes or No, amounts are rounded to 2 decimals and Draft Kept (%) is a percentage from 0 to 100.
startstringrequiredFirst day covered, YYYY-MM-DD (UTC).
endstringrequiredLast day covered, YYYY-MM-DD (UTC).
totalRowsnumberrequiredNumber of rows across all pages. For grains other than consultation it equals the number of rows returned.
pagenumberrequiredThe page returned. Always 1 unless grain=consultation.
totalPagesnumberrequiredOnly the consultation grain spans several pages; the others always return 1.
curl "https://vetnio.com/api/v2/organizations/3f6c2a1e-8b4d-4c7a-9e2f-5d1b7a9c0e42/data-export/usage" \
-H "Authorization: Bearer $VETNIO_API_KEY"{
"headers": [
"Date",
"Clinic ID",
"Clinic Name",
"Consultations"
],
"rows": [
[
"2026-09-01",
"furunculotic-loud-chihuahua",
"Riverside Animal Clinic",
412
]
],
"start": "2026-09-01",
"end": "2026-09-30",
"totalRows": 24,
"page": 1,
"totalPages": 1
}