Changelog
Every change to the /v1 contract, newest first. Additive changes ship without notice; see
Versioning for what that covers and what it does not.
2026-09-11 (wave 6b-3)
Video visits. A patient can now be booked into a live video consult with a licensed clinician.
GET /v1/visits/slots returns bookable slot starts for a patient's state over a window of at most
fourteen days, POST /v1/visits claims one, GET /v1/visits/{id} reads it back, and
POST /v1/visits/{id}/cancel and /reschedule move it while it is still scheduled; a reschedule
keeps the visit's id. GET /v1/visits/{id}/join mints the link the patient joins through: it is
patient-bound, lives fifteen minutes, opens an hour before the visit, and is meant to be fetched at
join time rather than stored. Two new scopes, visits:read and visits:write.
Six new event types ride the existing feed and your existing endpoints: visit.booked.v1,
visit.rescheduled.v1, visit.cancelled.v1, visit.started.v1, visit.completed.v1 and
visit.no_show.v1. A new charge kind, video_visit, is raised on visit.completed.v1 and on
nothing else; a no-show is unpriced for now. Test mode plays the clinician at
POST /v1/test/visits/{id}/start, /complete and /no_show, against sandbox availability
generated from a fixed weekday template.
Additive throughout: an existing caller is unaffected. The MCP server gains nine tools for the same operations, and both SDKs and the CLI carry the resource. See Visits, Events and realtime and Billing.
2026-09-11 (wave 6a-2)
A person can sign in from a machine with no browser. The authorization server now offers the
RFC 8628 device grant: POST /api/auth/device/code with a client_id, the scopes and a
resource, then a poll of /oauth2/token with
grant_type=urn:ietf:params:oauth:grant-type:device_code at the interval the server states. The
person confirms the code at developers.cuvo.co/device as a signed-in member with a second
factor, and approves the request whole or denies it: the device grant does not narrow. The CLI
uses it as cuvo login --device. A device token is a member's token, so it is test mode; live
data still needs a live client or a live key. Nothing about the /v1 contract changed. See
Authentication and CLI.
2026-09-11 (wave 6a-1)
The CLI listens locally, completes itself and scaffolds a starter. cuvo webhooks listen --forward-to http://localhost:3000/hooks subscribes to your integration's realtime channel and
POSTs every event to the local URL, signed with a session secret it prints once, so the handler
verifies with the SDK exactly as it will in production. It is a mirror of the event stream, not a
delivery: nothing is retried and no delivery row is written. cuvo completion zsh|bash|fish
prints a completion script from the command tree, and cuvo init [dir] writes a TypeScript or
Python starter with a sandbox smoke script and a verifying webhook handler. The TypeScript SDK
gains signWebhook, the sender's half of the signature scheme. Nothing about the /v1 contract
changed. See CLI.
2026-09-11 (wave 6a-3)
The MCP server publishes resources and every tool carries annotations. resources/list now
returns every guide on this site as cuvo://docs/<slug> (text/markdown) and the contract as
cuvo://openapi/v1.yaml (application/yaml); resources/read serves them, and a read leaves an
audit row exactly as a tool call does. Every tool declares the four MCP hints (readOnlyHint,
destructiveHint, idempotentHint, openWorldHint), derived from its operation's method and
verb, so a client that asks before a destructive call can ask correctly. docs_search stays.
Nothing about the /v1 contract changed. See MCP.
2026-09-09
The clients are published. Both are prerelease and both install with one command.
- TypeScript:
pnpm add @cuvo-health-us/api@next, version0.1.0-next.0. - Python:
pip install --pre cuvo, version0.1.0a1, on PyPI since 2026-09-08. - Command line:
pnpm add -g @cuvo-health-us/cli@next, version0.1.0-next.0.
The npm scope is @cuvo-health-us, not @cuvo. The @cuvo scope on npm belongs to someone
else, so the packages take the scope Cuvo owns. If your Cuvo contact handed you a tarball named
@cuvo/api or @cuvo/cli before today, uninstall it and install the published package: the code
is the same, the name is not. The cuvo command the CLI installs is unchanged, and so is the
Python package name.
Nothing about the /v1 contract changed with this release. See SDKs and
CLI.
2026-09-08 (wave 5b)
The patient export opened in live mode. POST /v1/patients/{id}/exports and GET /v1/patients/{id}/exports/{job} now answer in live mode, and the job comes back already
completed: a chart is bounded, so there is nothing to wait for. Its url is the new download
below, good for 24 hours, which is the life of the document links inside the bundle. Nothing about
the job shape changed, so a caller who wrote the polling loop keeps it.
GET /v1/exports/{id} and GET /v1/exports/{id}/download. Added. The first is the same job,
read by the id you kept rather than through the patient; the second streams the bundle as a file,
with the same credential and the same patients:read scope. The bundle holds the patient, their
cases, consents, files, prescriptions and orders in the shapes this API already publishes, plus a
download link per document.
410 gone. Added to the problem codes. A download past expires_at answers with it: the
export existed, and its bundle and the links inside it were deleted together. Start another one.
No other operation can answer 410. See Errors.
2026-09-08
OAuth clients. Added. A platform can now exchange a client id and secret for a one hour access
token at https://developers.cuvo.co/api/auth/oauth2/token and present it wherever an API key
goes. Dynamic client registration is open with PKCE required, and a portal member can consent an
agent to one integration in test mode. API keys are unchanged. See
Authentication.
MCP server. Added at https://mcp.cuvo.co/mcp. Thirty-seven tools over the same credential,
the same scopes and the same audit as the REST API. See MCP server.
GET /v1/invoices/{id}. Added. Returns the invoice the list already returned, plus charges:
one line per billable event, with its kind, the resource it was raised for, its amount and the
charge it reverses. GET /v1/invoices is unchanged.
Files require a patient. Changes a test-only shape: a breaking change for sandbox callers, and the only one in this release. Until today the file resource answered in test mode alone, so nothing in production spoke this shape and it could be corrected rather than versioned.
POST /v1/filesnow requirespatient. A request without it is refused with400 invalid_request, and theFileresource carries the field on every read.POST /v1/files/{id}/confirm, which previously took an empty body, now requirespatient,kindandfilename, repeating what the reservation was made with.mimeis now one ofapplication/pdf,image/jpeg,image/pngandimage/heicrather than any string.FileUploadgainedupload_headers. The upload URL is signed for them, so aPUTwithout them is refused by storage.
Files opened in live mode. POST /v1/files, its confirmation and GET /v1/files/{id} now
answer in live mode, which is what naming the patient bought. Three rules come with it:
- In live mode the file id changes at confirmation. What the reservation answered with
identifies the reservation; what the confirmation answers with identifies the filed document, and
on the chart those are two different things. Use the id from the response you last received. A
reservation is not readable through
GET /v1/files/{id}once it has been confirmed. In the sandbox the two ids happen to be the same, so code written against this rule works in both. DELETE /v1/files/{id}stays test only, and answers501 live_not_availablein live mode, because the internal clinical contract has no delete.- A live
POST /v1/casesstill refuses a non-emptyfilesarray.
See Files.
Developer documentation. Added: these guides, the reference, a
Postman collection, /llms.txt, /llms-full.txt, and the agent skill at
/skills/cuvo-api/SKILL.md.
Requesting access to an organization. Added to the developer portal. Send a clinic's org_…
id, the scopes you need and a note, and the request appears as a pending grant.
SDKs built; publication pending. The Python client, cuvo, is written and generated from the
same document as the TypeScript client, and both are built and gated in CI. Neither is published:
@cuvo-health-us/api@next and cuvo==0.1.0a1 are the names they will take, and until Cuvo releases them a
partner installs the build their Cuvo contact hands them. See SDKs.
2026-09-07
Billing reads. Added. GET /v1/usage aggregates the charge ledger over a window by kind, and
GET /v1/invoices mirrors the invoices raised against the integration. Both need billing:read and
both answer in either mode; a test credential gets an empty answer rather than a refusal. The
charge.created.v1 event, published with the v1 catalog, is emitted from this date. See
Billing.
2026-09-07 (earlier)
Live mode. Added for patients, consents, cases, messages, prescriptions, orders and the
catalog. A cuvo_sk_live_ credential reaches the clinical plane instead of the sandbox, once the
integration has accepted the business associate agreement in force. Nothing about the request or
response shape differs between the two modes. What is not yet open answers 501 live_not_available: the patient export, and at that date the whole file resource.
2026-09-06
/v1 published. The first public contract: organizations, catalog, patients, consents, files,
cases, messages, prescriptions, orders, webhook endpoints, events and realtime tokens, all in test
mode against the sandbox, with the simulated clinician and pharmacy under /v1/test. Cursor
pagination, RFC 9457 problem bodies, required idempotency keys on writes, and signed at-least-once
webhook delivery date from here.