VetFlash API
The VetFlash API gives your software the same veterinary AI that powers the VetFlash app: clinical chat, scribe notes, transcription and shareable Ask sessions. Requests are JSON over HTTPS and every call spends the credits on your VetFlash account at the in-app rate.
curl https://vetflash.io/api/v1/credits \
-H "Authorization: Bearer $VETFLASH_API_KEY"Authentication
Send a bearer token in the Authorization header. There are two kinds:
API keys
For your own servers. vf_live_… keys return real answers and spend credits; vf_test_… keys return sample responses and are always free. Create, scope, roll and revoke them in the API dashboard. Never ship a key in a browser or mobile app.
OAuth access tokens
For apps other vets connect to. vfat_… tokens act on behalf of the vet who approved your app, with only the scopes they granted, and spend their credits.
{
"error": {
"message": "Invalid or missing credentials.",
"type": "authentication_error",
"code": "invalid_api_key"
}
}OAuth 2.0 (authorization code + PKCE)
Register an app in OAuth apps to get a client ID (and a secret for confidential apps). PKCE with S256 is required for every client. Server metadata lives at https://vetflash.io/.well-known/oauth-authorization-server.
1. Send the vet to the consent screen
Generate a random
code_verifier(43-128 characters) and astate. The vet signs in, reviews the scopes and approves. We redirect back to your exact registeredredirect_uriwithcodeandstate.https://vetflash.io/oauth/authorize ?response_type=code &client_id=vfc_... &redirect_uri=https://yourapp.com/callback &scope=profile:read chat:write &state=RANDOM_STATE &code_challenge=BASE64URL(SHA256(code_verifier)) &code_challenge_method=S256
2. Exchange the code
Codes expire after 10 minutes and work once. Confidential clients authenticate with HTTP Basic (or
client_secretin the body); public clients sendclient_idonly.curl -X POST https://vetflash.io/api/oauth/token \ -u "$CLIENT_ID:$CLIENT_SECRET" \ -d grant_type=authorization_code \ -d code=vfac_... \ -d redirect_uri=https://yourapp.com/callback \ -d code_verifier=$CODE_VERIFIER{ "access_token": "vfat_...", "token_type": "Bearer", "expires_in": 3600, "refresh_token": "vfrt_...", "scope": "profile:read chat:write" }3. Refresh and revoke
Access tokens last one hour. Refresh tokens last 30 days and rotate on every use; presenting an already-used refresh token revokes the whole connection.
curl -X POST https://vetflash.io/api/oauth/token \ -u "$CLIENT_ID:$CLIENT_SECRET" \ -d grant_type=refresh_token \ -d refresh_token=vfrt_...
Scopes
| profile:read | Your name, email address and plan. |
| credits:read | Message, scribe, voice and FlashForm credit balances. |
| chat:write | Ask clinical questions. Spends message credits. |
| scribe:write | Turn consultations into clinical notes and transcripts. Spends scribe credits. |
| ask:read | List Ask sessions and read their conversations. |
| ask:write | Create and revoke shareable patient chats. Messages spend message credits. |
API keys get all 6 scopes unless you narrow them. A call without the required scope returns 403 with code: "insufficient_scope".
Credits & billing
Calls spend the credits on the account behind the key or token, at exactly the in-app rate. Every response reports what it cost (X-VetFlash-Credits-Charged header or a vetflash / credits_charged field). Failed generations are refunded automatically, and max_credits rejects a chat request that would cost more than you allow.
| Chat model | What it is | Credits |
|---|---|---|
| vetflash | Our fine-tuned veterinary model with medicines-database lookups and a senior clinical review pass. | 10 |
| vetflow | The practice-operations consultant: rotas, HR, pricing, client comms. | 30 |
Legacy model names still work: vetflash-ask → vetflash, vetflash-vetflow → vetflow.
Errors
Errors share one shape. Every response carries an X-Request-Id; include it when you contact us.
| 400 | invalid_request_error | The request is malformed or missing a field. |
| 401 | authentication_error | Missing, invalid, expired or revoked key or token. |
| 402 | insufficient_credits | Not enough credits, or the request exceeds `max_credits`. |
| 403 | permission_error | The token lacks the scope, or your plan doesn't include the feature. |
| 404 | not_found_error | The resource doesn't exist or isn't yours. |
| 429 | rate_limit_error | More than 60 requests a minute. Retry after `Retry-After` seconds. |
| 500 | server_error | Something failed on our side. Credits are refunded automatically. |
Rate limits
60 requests per 60 seconds per API key or OAuth connection. Over the limit you get 429 with a Retry-After header. Ask session links are separately limited to 20 visitor messages a minute.
Streaming
Set "stream": true on chat completions to receive server-sent events in the OpenAI format. VetFlash M2 streams its reasoning as delta.reasoning_content while it researches, then the clinically reviewed answer as delta.content. The final chunk includes the credits charged.
data: {"object":"chat.completion.chunk","choices":[{"delta":{"role":"assistant","content":""}}]}
data: {"object":"chat.completion.chunk","choices":[{"delta":{"reasoning_content":"Considering GI vs systemic causes..."}}]}
data: {"object":"chat.completion.chunk","choices":[{"delta":{"content":"Top differentials: "}}]}
data: {"object":"chat.completion.chunk","choices":[{"delta":{},"finish_reason":"stop"}],"vetflash":{"credits_charged":10}}
data: [DONE]Account
Who am I
GET/api/v1/me
The account behind the key or token, its plan and the scopes this request carries.
curl https://vetflash.io/api/v1/me \
-H "Authorization: Bearer $VETFLASH_API_KEY"Response
{
"id": "5b0c…",
"email": "vet@practice.co.uk",
"name": "Dr Sam Reid",
"plan": "VetFlash Pro",
"auth": {
"method": "api_key",
"sandbox": false,
"scopes": [
"profile:read",
"credits:read"
]
}
}Credit balance
GET/api/v1/credits
Live balances for every credit type, plus your plan's monthly allowance.
curl https://vetflash.io/api/v1/credits \
-H "Authorization: Bearer $VETFLASH_API_KEY"Response
{
"credits": {
"text": 1240,
"scribe": 96.5,
"voice": 24,
"flashform": 15000
},
"allowance": {
"text": 1500,
"scribe": 100,
"voice": 30,
"flashform": 0
},
"last_reset_at": "2026-09-01T00:00:00Z"
}AI
Chat completions
POST/api/v1/chat/completions
OpenAI-compatible chat with VetFlash models. Point any OpenAI SDK at the base URL and it just works. Set stream: true for server-sent events.
- modelstringrequired
vetflashorvetflow.- messagesarrayrequired
- Conversation so far:
{ role, content }with rolessystem,user,assistant. - streamboolean
- Stream
chat.completion.chunkevents. Reasoning arrives asdelta.reasoning_content. - patient_idinteger
- One of your patients: adds species, breed and age to the context.
- max_creditsnumber
- Reject with 402 instead of spending more than this.
curl -X POST https://vetflash.io/api/v1/chat/completions \
-H "Authorization: Bearer $VETFLASH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "vetflash",
"messages": [
{
"role": "user",
"content": "Maintenance fluid rate for a 12 kg dog?"
}
],
"max_credits": 20
}'Response
{
"id": "chatcmpl-vf-…",
"object": "chat.completion",
"model": "vetflash",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "For a 12 kg dog…"
},
"finish_reason": "stop"
}
],
"vetflash": {
"credits_charged": 10,
"sandbox": false
}
}Scribe notes
POST/api/v1/scribe/notes
Turn a consultation into a structured clinical note. Send a transcript as JSON, or upload the audio as multipart file and we transcribe it too.
- transcriptstring
- Consultation transcript. Required unless you upload
file. - duration_secondsinteger
- Length of the consultation. Billing never goes below what the transcript length implies.
- fileaudio (multipart)
- Audio recording instead of a transcript.
- formatstring
markdown(default) ortext.- languagestring
- Audio language hint for uploads, e.g.
en-GB.
curl -X POST https://vetflash.io/api/v1/scribe/notes \
-H "Authorization: Bearer $VETFLASH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"transcript": "Owner reports Bella, a 6 year old Labrador, has been limping on the left fore for three days…",
"duration_seconds": 540,
"format": "markdown"
}'Response
{
"id": "note_…",
"note": "## Presenting complaint\n- Left forelimb lameness, 3 days…",
"duration_seconds": 540,
"credits_charged": 1
}Transcribe audio
POST/api/v1/audio/transcriptions
OpenAI-compatible speech-to-text for consultation recordings. Upload multipart file.
- fileaudio (multipart)required
- The recording (mp3, m4a, wav, webm…), up to 45 minutes.
- languagestring
- Language hint, e.g.
en-GB.
curl -X POST https://vetflash.io/api/v1/audio/transcriptions \
-H "Authorization: Bearer $VETFLASH_API_KEY" \
-F "file=@consultation.m4a" \
-F "language=en-GB"Response
{
"text": "Owner reports Bella has been limping…",
"duration_seconds": 540,
"vetflash": {
"credits_charged": 1,
"sandbox": false
}
}Ask sessions
Create an Ask session
POST/api/v1/ask/sessions
A shareable chat link seeded with a patient, for a colleague or pet owner. You set the expiry and a credit cap; messages bill to you.
- patient_idinteger
- One of your patients. Or send
patient/contextinstead. - patientobject
{ pet_name, species, breed, age, notes }.- contextstring
- Free-text clinical context.
- titlestring
- Shown at the top of the chat.
- welcome_messagestring
- First message the visitor sees.
- languagestring
- Answer language, e.g.
Spanish. - expires_ininteger
- Seconds until the link expires (default 7 days, max 90).
- credit_limitnumber
- Most credits the session may spend (default 100).
- max_messagesinteger
- Optional cap on visitor messages.
curl -X POST https://vetflash.io/api/v1/ask/sessions \
-H "Authorization: Bearer $VETFLASH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Bella - post-op questions",
"patient": {
"pet_name": "Bella",
"species": "Dog",
"breed": "Labrador",
"age": "6 years"
},
"welcome_message": "Hi! Ask me anything about Bella'\''s recovery.",
"expires_in": 604800,
"credit_limit": 60
}'Response
{
"token": "0f7c…",
"url": "https://vetflash.io/ask/0f7c…",
"state": "active",
"credit_limit": 60,
"credits_used": 0,
"expires_at": "2026-09-22T09:00:00Z"
}List Ask sessions
GET/api/v1/ask/sessions
Newest first. Page with limit (max 100) and the next_cursor from the previous page.
- limitinteger
- Page size, 1-100 (default 20).
- cursorstring
next_cursorfrom the previous page.
curl https://vetflash.io/api/v1/ask/sessions \
-H "Authorization: Bearer $VETFLASH_API_KEY"Response
{
"data": [
{
"token": "0f7c…",
"title": "Bella - post-op questions",
"state": "active"
}
],
"next_cursor": null
}Retrieve an Ask session
GET/api/v1/ask/sessions/{token}
The session with its full conversation.
curl https://vetflash.io/api/v1/ask/sessions/SESSION_TOKEN \
-H "Authorization: Bearer $VETFLASH_API_KEY"Response
{
"token": "0f7c…",
"state": "active",
"messages": [
{
"role": "user",
"content": "Can she climb stairs yet?",
"created_at": "2026-09-15T10:02:00Z"
}
]
}Revoke an Ask session
DELETE/api/v1/ask/sessions/{token}
The link stops working immediately. The conversation stays readable to you.
curl -X DELETE https://vetflash.io/api/v1/ask/sessions/SESSION_TOKEN \
-H "Authorization: Bearer $VETFLASH_API_KEY"Response
{
"token": "0f7c…",
"state": "revoked"
}Ready to build?
Create a free test key and try every endpoint from the playground.
Open the playground