Clients & Patients
This guide explains how to work with client (owner) and patient (animal) data through the Provet REST API.
In Provet, a client is the owner or guardian of one or more animals. A patient is the animal being treated. Clients and patients are separate resources: a client can have multiple patients, but each patient belongs to exactly one client. Both resources support full create, read, update, and archive operations via the REST API.
Fetching Clients
API endpoint: https://provetcloud.com/<provet_id>/api/0.1/client/
Use query parameters to filter the client list:
Parameter |
Example |
Description |
|---|---|---|
|
|
Case-insensitive partial match on first name |
|
|
Case-insensitive partial match on last name |
|
|
Partial match on company/organization name |
|
|
Exact match on email address |
|
|
Partial match on any phone number |
|
|
Exclude archived clients |
|
|
Only clients with at least one living patient |
|
|
Records changed since this timestamp (use for delta sync) |
Searching by name across individual and corporate clients:
The default behaviour combines filters with AND logic. To search across
firstname, lastname, and organization_name simultaneously with OR
logic — useful when you don’t know whether a client is a person or a company
— add filter_type=or:
GET /api/0.1/client/?firstname__icontains=hill&filter_type=or
This returns any client whose first name, last name, or organisation name contains “hill”.
Creating a Client
API endpoint: POST https://provetcloud.com/<provet_id>/api/0.1/client/
Request Body Example:
{
"firstname": "John",
"lastname": "Smith",
"email": "john.smith@example.com",
"country": "GB",
"customer_type": 0
}
Customer type values:
Value |
Description |
|---|---|
0 |
Private person |
1 |
Company |
2 |
EU Company |
3 |
Charitable organization |
4 |
UK charity |
Note
The country field uses ISO 3166-1 alpha-2 codes (e.g., FI, GB,
US, NO).
Phone numbers are not part of the client record itself — they are created
as separate records linked to the client, and each client response lists them
in phone_numbers. See the Phone Numbers section below.
Updating a Client
API endpoint: PATCH https://provetcloud.com/<provet_id>/api/0.1/client/<id>/
Only include the fields you want to change. Example — updating the email address and disabling SMS:
{
"email": "new.email@example.com",
"no_sms": true
}
Setting no_sms=true or no_email=true prevents Provet from sending
automated messages to this client.
Fetching Patients
API endpoint: https://provetcloud.com/<provet_id>/api/0.1/patient/
Commonly used filters:
Parameter |
Example |
Description |
|---|---|---|
|
|
All patients belonging to a specific client |
|
|
Case-insensitive partial match on patient name |
|
|
Exact microchip number lookup |
|
|
Filter by species code — numeric ID from |
|
|
Exclude archived patients |
|
|
Only living (non-deceased) patients |
|
|
Patients born on or after this date |
|
|
Records changed since this timestamp |
Example — fetch all living, non-archived patients for a specific client:
GET /api/0.1/patient/?client__is=456&archived__is=false&deceased__is_null=true
Creating a Patient
API endpoint: POST https://provetcloud.com/<provet_id>/api/0.1/patient/
Request Body Example:
{
"client": "https://provetcloud.com/<provet_id>/api/0.1/client/456/",
"name": "Max",
"species": 15461002,
"breed": 13689002,
"gender": "Male",
"date_of_birth": "2020-06-15"
}
Note
species and breed accept a numeric code — the id field
from the codelist response. Retrieve the list of valid species codes from
GET /api/0.1/codelist/species/ and valid breed codes from
GET /api/0.1/codelist/breeds/. Use the id field from each result
as the value. Codes are installation-specific and may differ between
Provet environments.
The species field in the response returns the human-readable display
name (e.g. "Dog (Canine - Domestic)"); to get the numeric code back,
read the species_code field instead.
Gender values:
Value |
Notes |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
Equine only |
|
Equine only |
|
Equine only |
Example response:
{
"id": 2130,
"url": "https://provetcloud.com/4/api/0.1/patient/2130/",
"client": "https://provetcloud.com/4/api/0.1/client/218/",
"name": "Fenton",
"official_name": "",
"species": "Dog (Canine - Domestic)",
"breed": "None",
"gender": "Female",
"weight": null,
"date_of_birth": null,
"deceased": null,
"microchip": null,
"archived": false,
"home_department": null,
"created": "2021-11-10T15:45:50.179386+02:00",
"modified": "2021-11-10T15:45:50.179414+02:00",
"tags_rel": [],
"fields_rel": []
}
Updating a Patient
API endpoint: PATCH https://provetcloud.com/<provet_id>/api/0.1/patient/<id>/
Example — recording a microchip and current weight:
{
"microchip": "900123456789012",
"weight": "32.5"
}
To mark a patient as deceased, set the deceased field to a date:
{
"deceased": "2024-03-20"
}
To reactivate a patient that was incorrectly marked as deceased, set
deceased to null.
Custom Fields
Custom fields allow clinics to store additional data on clients and patients beyond the standard fields. The available fields differ between Provet installations.
Discovering Available Fields
GET /api/0.1/custom_fields/
Returns all custom field definitions configured for this Provet instance.
Key response fields:
Field |
Description |
|---|---|
|
The field ID — used when writing values |
|
Human-readable label |
|
Data type of the field, as an integer. See the table below. |
|
Which model this field applies to: |
|
True when your application created this field, so may change or archive it |
field_type values. All seven can appear when reading; only some can be
created through the API:
Value |
Type |
Can be created through the API |
Format of a value |
|---|---|---|---|
|
Text |
Yes |
Any text |
|
Number |
Yes |
A whole number. Store decimals in a text field. |
|
Date |
Yes |
|
|
Check box |
Yes |
|
|
JSON |
Yes |
A JSON-encoded string |
|
Select |
No |
A list of options, see the select example below |
|
Multi-select |
No |
A list of options, see the select example below |
Managing Field Definitions
An integration can create and manage its own custom field definitions, so a clinic does not have to add them by hand before your integration can store data.
POST /api/0.1/custom_fields/
{
"name": "Loyalty scheme reference",
"content_type": "client",
"field_type": 1,
"default_value": "Bronze"
}
content_type accepts client, patient or consultation. Creation
supports the field types whose answer is a single value: text, number, date,
check box and JSON. Select and multi-select fields carry their own options and
can only be set up in Provet; sending one returns 400.
default_value is optional. It is the value Provet fills in when
staff create a new record, and it is always sent and returned as a string:
Field type |
Format |
Example |
|---|---|---|
Text |
Any text |
|
Number |
A whole number, up to 15 digits |
|
Date |
|
|
Check box |
|
|
JSON fields cannot have a default. A value that does not fit the field type
returns 400. An empty string means there is no default.
Note
Definition management requires an OAuth 2.0 access token. A legacy API key cannot create or change definitions, because every field records which application created it.
Creating, changing and archiving definitions also needs the “Custom field
settings” write permission, in addition to “Patients and clients”. A field
applies to every department, so this is the same permission the custom field
settings page needs. The user the access token acts for must have both; with
an authorization code token, that is the user who authorized your
application. A request without them returns 403.
A legacy API key returns 401 on these requests. Permissions are checked
first, though, so a legacy API key whose permission group lacks the
permissions above returns 403 instead. Either way, use an OAuth 2.0
access token.
Your application may only change or archive the fields it created itself.
Fields created by clinic staff, or by another integration, are readable but not
writable, and return 403. The owned flag on each field tells you which
are yours, so you do not have to attempt a write to find out.
PATCH /api/0.1/custom_fields/<id>/
name, default_value and archived can be changed. Send an empty
default_value to remove the default. is_required is read-only: it is
ignored when you create or change a field, and is set by the clinic in
Provet.
field_type and content_type are fixed when the field is created, because
changing either would leave values already recorded against the field with data
that no longer matches it. Sending a different value returns 400; sending the
current value is accepted, so echoing a whole record back is safe. There is no
PUT on this endpoint for the same reason.
A name that is already taken for that content_type returns 400, including
when the existing field is archived. When the existing field is one your
application created, the error gives its id. If that field is one your
integration archived earlier, restore it with PATCH and
{"archived": false} instead of creating a new one.
DELETE /api/0.1/custom_fields/<id>/
Archives the definition. It is never deleted, and the values clinics have
already recorded against it are preserved. Restore an archived field by sending
PATCH with {"archived": false}; its values are still attached where they
were.
Values recorded against an archived field can still be read and written through the API, although Provet’s forms hide the field.
Warning
A field name must be unique for each content_type across the whole
installation, and an archived field keeps its name reserved. If you archive
“External ID” for clients you cannot create a new client field with that name
until the archived one is restored. Names are also shared with the clinic’s
own fields, so creating a field may return 400 if staff already added one
with that name.
Note
Clinic staff can still rename or archive a field your application created,
from the custom field settings page. Look up fields by id, not by name.
POST /api/0.1/consultation/admit/ matches custom fields by name, so after
a rename a request that still uses the old name returns 400.
A field archived by clinic staff stays owned by your application, so the API still lets you restore it. Restore only fields your integration archived itself; a field the clinic archived was archived on purpose.
Reading Custom Field Values
Custom field values are always included in the standard client and
patient response as a fields_rel array. Each entry contains:
Field |
Description |
|---|---|
|
ID of this value record |
|
ID of the custom field definition |
|
Human-readable field name |
|
The stored value |
If fields_rel returns an empty list, it means either no custom fields
have been configured on the instance, or no values have been set for that
record yet.
You can also fetch custom field values from the dedicated nested endpoints:
GET /api/0.1/client/<id>/custom_field_values/
GET /api/0.1/patient/<id>/custom_field_values/
Writing Custom Field Values
Include the fields_rel array in a create or update request body:
{
"firstname": "John",
"lastname": "Smith",
"fields_rel": [
{
"field_id": 42,
"value": "Referred by Dr. Brown"
}
]
}
For select or multi-select fields, pass value as a list of option
objects:
{
"fields_rel": [
{
"field_id": 55,
"value": [
{"id": 101, "name": "Option A"},
{"id": 102, "name": "Option B"}
]
}
]
}
You can also create or update values via the nested endpoint:
POST /api/0.1/client/<id>/custom_field_values/
For JSON fields, send value as a JSON-encoded string, for example
"{\"ref\": 1}". The custom_field_values endpoints reject an object
sent as value with 400.
Consultation fields
To write a value to a consultation field, use the flat endpoint with the
consultation ID as object_id:
POST /api/0.1/custom_field_values/
{
"field": 77,
"object_id": 1234,
"value": "Referred by Dr. Brown"
}
The value’s content_type comes from the field. If the record already has a
value for that field, the existing value is updated.
Note
If a custom field value already exists for that field on that record, posting to the nested endpoint will update the existing value rather than returning a duplicate error.
Phone Numbers
Phone numbers are stored as separate records linked to a client, not as fields on the client record itself.
Add a phone number after creating the client:
API endpoint: POST https://provetcloud.com/<provet_id>/api/0.1/phonenumber/
{
"client": "https://provetcloud.com/<provet_id>/api/0.1/client/456/",
"phone_number": "+441234567890",
"type_code": 1
}
Phone type values:
Value |
Description |
|---|---|
0 |
Landline |
1 |
Mobile |
To list all phone numbers for a client:
GET /api/0.1/phonenumber/?client__is=456
Bulk Patient Creation
To create multiple patients in a single request, use the bulk endpoint:
API endpoint: POST https://provetcloud.com/<provet_id>/api/0.1/patient/bulk/
The request body is an array of patient objects using the same fields as the standard create endpoint.
Add ?force_push=true to enable partial success mode: patients that pass
validation are created even if others in the batch fail.
Response status codes:
Status |
Meaning |
|---|---|
201 |
All patients created successfully |
207 |
Partial success — check response for which records failed |
400 |
All records failed validation |
Recommended Sync Pattern
This section describes a practical approach for keeping an external system in sync with Provet client and patient data. Keeping your data in sync explains each step in more detail.
Step 1: Read clients and patients once
On the first run, read both lists in full. Read them in id order, asking each time
for the records after the largest id you have, until a page comes back empty (see
Keeping your data in sync):
GET /api/0.1/client/?id__gt=0&page_size=200
GET /api/0.1/patient/?id__gt=0&page_size=200
Step 2: Poll for changes
On every later run, ask both lists for what changed since the start of your last successful run, a few minutes earlier to allow for overlap (see Keeping your data in sync):
GET /api/0.1/client/?modified__gte=<start of last successful run>
GET /api/0.1/patient/?modified__gte=<start of last successful run>
Read patients from their own list, not one client at a time with client__is: a patient
can change without its client changing, and one request per client uses up the rate
limit quickly. Include a time zone in the timestamp, for example Z for UTC.
Step 3: Handle archived records
Deleting a client or patient, through the API or in Provet, archives it:
archived is set to true, and the record appears in the next “changed since”
request. Treat archived=true as the soft-delete
signal in your external system.
A client whose data is erased on request disappears from the API, so it can’t appear in a “changed since” request. Subscribe to the “Client delete” webhook to hear about these.
Step 4: Custom fields and phone numbers
Custom field values are included in every client and patient response (fields_rel),
and each client response lists the client’s phone numbers (phone_numbers), so you
don’t need to fetch them separately on the first run. Changing them on their own doesn’t
change the client’s or patient’s modified time, though, so a “changed since” request
may not return the record.
To pick up changed custom field values, read them from their own list:
GET /api/0.1/custom_field_values/?modified__gte=<start of last successful run>
Each value names its field and the object_id of the record it belongs to. The
field definition (/api/0.1/custom_fields/<id>/) says in content_type whether that
is a client, a patient or a consultation. The list includes all three, so skip the values
of fields you don’t sync.
Phone numbers have no modified filter. Subscribe to the “Phone number” webhook to hear
about phone number changes. See Keeping your data in sync for other changes that this approach
doesn’t show.
Note
Consider using webhooks for client and patient update events to avoid constant polling. Webhooks notify your system when a record changes, usually within moments, though delivery time isn’t guaranteed. Keep a daily “changed since” run as well, in case a webhook delivery fails. See Webhooks for configuration details and List of Webhook Triggers for the list of available events.