Clients & Patients
This guide explains how to work with client (owner) and patient (animal) data through the Vetera REST API.
In Vetera, 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://app.vetera.com.au/<vetera_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://app.vetera.com.au/<vetera_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. See the Phone Numbers section below.
Updating a Client
API endpoint: PATCH https://app.vetera.com.au/<vetera_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 Vetera from sending
automated messages to this client.
Fetching Patients
API endpoint: https://app.vetera.com.au/<vetera_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://app.vetera.com.au/<vetera_id>/api/0.1/patient/
Request Body Example:
{
"client": "https://app.vetera.com.au/<vetera_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
Vetera 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://app.vetera.com.au/4/api/0.1/patient/2130/",
"client": "https://app.vetera.com.au/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://app.vetera.com.au/<vetera_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 Vetera installations.
Discovering Available Fields
GET /api/0.1/custom_fields/
Returns all custom field definitions configured for this Vetera 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 Vetera; sending one returns 400.
default_value is optional. It is the value Vetera 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
Vetera.
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 Vetera’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://app.vetera.com.au/<vetera_id>/api/0.1/phonenumber/
{
"client": "https://app.vetera.com.au/<vetera_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://app.vetera.com.au/<vetera_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 Vetera client and patient data.
Step 1: Poll for changed clients
GET /api/0.1/client/?modified__gte=<last_sync_timestamp>
Store the current timestamp before each sync run and use it as
modified__gte on the next run. This returns only records that changed
since your last sync.
Step 2: Fetch patients for changed clients
For each changed client, fetch their patients:
GET /api/0.1/patient/?client__is=<client_id>&modified__gte=<last_sync_timestamp>
Alternatively, poll patients independently on the same modified timestamp:
GET /api/0.1/patient/?modified__gte=<last_sync_timestamp>
Step 3: Handle archived records
Records are never hard-deleted via the API. When a client or patient is
removed from active use in Vetera, archived is set to true. Treat
archived=true as the soft-delete signal in your external system.
Step 4: Sync custom fields if needed
If your integration needs custom field values, fetch them via the nested endpoint for each record:
GET /api/0.1/client/<id>/custom_field_values/
Note
Consider using webhooks for client and patient update events to avoid constant polling. Webhooks notify your system immediately when a record changes. See Webhooks for configuration details and List of Webhook Triggers for the list of available events.